Cách hiệu quả nhất để lưu trữ một danh sách trong các mô hình Django là gì?


146

Hiện tại tôi có rất nhiều đối tượng python trong mã của mình tương tự như sau:

class MyClass():
  def __init__(self, name, friends):
      self.myName = name
      self.myFriends = [str(x) for x in friends]

Bây giờ tôi muốn biến mô hình này thành mô hình Django, trong đó self.myName là trường chuỗi và self.myFriends là danh sách các chuỗi.

from django.db import models

class myDjangoModelClass():
    myName = models.CharField(max_length=64)
    myFriends = ??? # what goes here?

Vì danh sách này là một cấu trúc dữ liệu phổ biến trong python, nên tôi dự kiến ​​sẽ có một trường mô hình Django cho nó. Tôi biết tôi có thể sử dụng mối quan hệ ManyToMany hoặc OneToMany, nhưng tôi đã hy vọng tránh được sự gián tiếp thêm đó trong mã.

Biên tập:

Tôi đã thêm câu hỏi liên quan này , mà mọi người có thể thấy hữu ích.


1
@drozzy: Có lẽ tôi có thể đã sử dụng một cụm từ khác, nhưng về cơ bản, ý tôi là, tôi muốn chuyển vào một danh sách các chuỗi và lấy lại danh sách các chuỗi. Tôi không muốn tạo ra một loạt các đối tượng Bạn bè và gọi inst.myFriends.add (friendObj) cho mỗi đối tượng. Không phải là nó sẽ khó đến thế đâu, nhưng ...
đau buồn

Câu trả lời:


77

Mối quan hệ này sẽ không được thể hiện tốt hơn dưới dạng mối quan hệ một-nhiều-ngoại đối với một Friendsbảng? Tôi hiểu rằng đó myFriendschỉ là các chuỗi nhưng tôi nghĩ rằng một thiết kế tốt hơn sẽ là tạo ra một Friendmô hình và có MyClasschứa một thực tế khóa ngoại cho bảng kết quả.


15
Đây có lẽ là những gì tôi sẽ làm, nhưng tôi thực sự hy vọng cấu trúc cơ bản cho việc này sẽ được xây dựng. Tôi đoán tôi sẽ lười biếng.
đau buồn

Thanh lịch và đẹp nhất khám phá.
Tessaracter


129

"Tối ưu hóa sớm là gốc rễ của mọi tội lỗi."

Với ý nghĩ chắc chắn, hãy làm điều này! Khi ứng dụng của bạn đạt đến một điểm nhất định, dữ liệu không chuẩn hóa là rất phổ biến. Hoàn thành chính xác, nó có thể tiết kiệm rất nhiều tra cứu cơ sở dữ liệu đắt tiền với chi phí cho việc dọn phòng nhiều hơn một chút.

Để trả về listtên bạn bè, chúng tôi cần tạo một lớp Django Field tùy chỉnh sẽ trả về danh sách khi được truy cập.

David Cramer đã đăng một hướng dẫn để tạo SeperatedValueField trên blog của mình. Đây là mã:

from django.db import models

class SeparatedValuesField(models.TextField):
    __metaclass__ = models.SubfieldBase

    def __init__(self, *args, **kwargs):
        self.token = kwargs.pop('token', ',')
        super(SeparatedValuesField, self).__init__(*args, **kwargs)

    def to_python(self, value):
        if not value: return
        if isinstance(value, list):
            return value
        return value.split(self.token)

    def get_db_prep_value(self, value):
        if not value: return
        assert(isinstance(value, list) or isinstance(value, tuple))
        return self.token.join([unicode(s) for s in value])

    def value_to_string(self, obj):
        value = self._get_val_from_obj(obj)
        return self.get_db_prep_value(value)

Logic của mã này liên quan đến các giá trị tuần tự hóa và giải tuần tự hóa từ cơ sở dữ liệu sang Python và ngược lại. Bây giờ bạn có thể dễ dàng nhập và sử dụng trường tùy chỉnh của chúng tôi trong lớp mô hình:

from django.db import models
from custom.fields import SeparatedValuesField 

class Person(models.Model):
    name = models.CharField(max_length=64)
    friends = SeparatedValuesField()

8
+1 cho một câu trả lời tuyệt vời, nhưng chúng tôi đã làm một cái gì đó như thế này. Nó thực sự squishing tất cả các giá trị thành một chuỗi sau đó tách chúng ra. Tôi đoán rằng tôi đã hy vọng một cái gì đó giống như ListofStringsField, thực sự xây dựng bảng riêng biệt và tự động tạo các khóa ngoại. Tôi không chắc nếu điều đó là có thể trong Django. Nếu có, và tôi tìm thấy câu trả lời, tôi sẽ đăng nó trên stackoverflow.
đau buồn

2
Nếu đó là trường hợp thì bạn đang tìm kiếm django-denorm của initcrash. Bạn sẽ tìm thấy nó trên github: github.com/initcrash/django-denorm/tree/master
jb.

3
+1. Nhưng vấn đề có thể xảy ra với dấu phẩy trong chuỗi. Điều gì về serialization và deserializing từ json?
sbeliakov

Cố gắng thêm mô hình này vào mô hình hiện có my_vals = SeparatedValuesField(blank=True, default="")nhưng nhận IntegrityError vì NULL. Là đối số mặc định không được thông qua chính xác?
John Lehmann

1
Lưu ý rằng trong Django 2.1 to_pythonkhông còn được gọi khi đọc. Do đó, để thực hiện công việc này, bạn cần thêm: def from_db_value(self, value, expression, connection, context): return self.to_python(value)
theadriangreen

46

Một cách đơn giản để lưu trữ một danh sách trong Django là chỉ cần chuyển đổi nó thành một chuỗi JSON, sau đó lưu nó dưới dạng Văn bản trong mô hình. Sau đó, bạn có thể truy xuất danh sách bằng cách chuyển đổi chuỗi (JSON) trở lại danh sách python. Đây là cách thực hiện:

"Danh sách" sẽ được lưu trữ trong mô hình Django của bạn như vậy:

class MyModel(models.Model):
    myList = models.TextField(null=True) # JSON-serialized (text) version of your list

Trong mã xem / mã điều khiển của bạn:

Lưu trữ danh sách trong cơ sở dữ liệu:

import simplejson as json # this would be just 'import json' in Python 2.7 and later
...
...

myModel = MyModel()
listIWantToStore = [1,2,3,4,5,'hello']
myModel.myList = json.dumps(listIWantToStore)
myModel.save()

Lấy danh sách từ cơ sở dữ liệu:

jsonDec = json.decoder.JSONDecoder()
myPythonList = jsonDec.decode(myModel.myList)

Về mặt khái niệm, đây là những gì đang diễn ra:

>>> myList = [1,2,3,4,5,'hello']
>>> import simplejson as json
>>> myJsonList = json.dumps(myList)
>>> myJsonList
'[1, 2, 3, 4, 5, "hello"]'
>>> myJsonList.__class__
<type 'str'>
>>> jsonDec = json.decoder.JSONDecoder()
>>> myPythonList = jsonDec.decode(myJsonList)
>>> myPythonList
[1, 2, 3, 4, 5, u'hello']
>>> myPythonList.__class__
<type 'list'>

8
Thật không may, điều này không giúp bạn quản lý danh sách bằng cách sử dụng quản trị viên django
GreenAsJade

25

Nếu bạn đang sử dụng Django> = 1.9 với Postgres, bạn có thể sử dụng các lợi thế của ArrayField

Một trường để lưu trữ danh sách dữ liệu. Hầu hết các loại trường có thể được sử dụng, bạn chỉ cần chuyển một thể hiện trường khác là base_field. Bạn cũng có thể chỉ định một kích thước. ArrayField có thể được lồng nhau để lưu trữ các mảng đa chiều.

Cũng có thể lồng các trường mảng:

from django.contrib.postgres.fields import ArrayField
from django.db import models

class ChessBoard(models.Model):
    board = ArrayField(
        ArrayField(
            models.CharField(max_length=10, blank=True),
            size=8,
        ),
        size=8,
    )

Như @ thane-brimhall đã đề cập, cũng có thể truy vấn các phần tử trực tiếp. Tài liệu tham khảo


2
Ưu điểm lớn của việc này là bạn có thể truy vấn các phần tử trực tiếp từ trường mảng.
Thane Brimhall

@ThaneBrimhall bạn nói đúng. Có lẽ tôi nên cập nhật câu trả lời với thông tin này. Cảm ơn
wolendranh

Đáng buồn thay, không có giải pháp nào cho mysql
Joel G Mathew

Cần phải đề cập rằng điều này chỉ hoạt động với PostGres.
theadriangreen


15

Vì đây là một câu hỏi cũ và các kỹ thuật Django phải thay đổi đáng kể từ đó, câu trả lời này phản ánh phiên bản Django 1.4, và rất có thể áp dụng cho phiên bản 1.5.

Django theo mặc định sử dụng cơ sở dữ liệu quan hệ; bạn nên tận dụng chúng. Ánh xạ mối quan hệ bạn bè với các mối quan hệ cơ sở dữ liệu (các ràng buộc khóa ngoài) với việc sử dụng ManyToManyField. Làm như vậy cho phép bạn sử dụng Trình quản lý liên quan cho danh sách bạn bè, sử dụng truy vấn thông minh. Bạn có thể sử dụng tất cả các phương thức có sẵn như filterhoặc values_list.

Sử dụng ManyToManyFieldquan hệ và tài sản:

class MyDjangoClass(models.Model):
    name = models.CharField(...)
    friends = models.ManyToManyField("self")

    @property
    def friendlist(self):
        # Watch for large querysets: it loads everything in memory
        return list(self.friends.all())

Bạn có thể truy cập danh sách bạn bè của người dùng theo cách này:

joseph = MyDjangoClass.objects.get(name="Joseph")
friends_of_joseph = joseph.friendlist

Tuy nhiên, lưu ý rằng các mối quan hệ này là đối xứng: nếu Joseph là bạn của Bob, thì Bob là bạn của Joseph.


9
class Course(models.Model):
   name = models.CharField(max_length=256)
   students = models.ManyToManyField(Student)

class Student(models.Model):
   first_name = models.CharField(max_length=256)
   student_number = models.CharField(max_length=128)
   # other fields, etc...

   friends = models.ManyToManyField('self')

8

Hãy nhớ rằng điều này cuối cùng phải kết thúc trong một cơ sở dữ liệu quan hệ. Vì vậy, sử dụng quan hệ thực sự cách phổ biến để giải quyết vấn đề này. Nếu bạn hoàn toàn khăng khăng lưu trữ một danh sách trong chính đối tượng, bạn có thể làm cho nó được phân tách bằng dấu phẩy và lưu nó trong một chuỗi, sau đó cung cấp các hàm truy cập chia chuỗi thành một danh sách. Cùng với đó, bạn sẽ bị giới hạn số lượng chuỗi tối đa và bạn sẽ mất các truy vấn hiệu quả.


3
Tôi ổn với cơ sở dữ liệu lưu trữ nó như một mối quan hệ, tôi đã hy vọng các mô hình Django đã trừu tượng hóa phần đó cho tôi. Từ phía ứng dụng, tôi luôn muốn coi nó như một danh sách các chuỗi.
đau buồn



3

Lưu trữ một danh sách các chuỗi trong mô hình Django:

class Bar(models.Model):
    foo = models.TextField(blank=True)

    def set_list(self, element):
        if self.foo:
            self.foo = self.foo + "," + element
        else:
            self.foo = element

    def get_list(self):
        if self.foo:
            return self.foo.split(",")
        else:
            None

và bạn có thể gọi nó như thế này:

bars = Bar()
bars.set_list("str1")
bars.set_list("str2")
list = bars.get_list()
if list is not None:
    for bar in list:
        print bar
else:
    print "List is empty."      

2

Giải pháp của tôi, có thể là nó giúp được ai đó:

import json
from django.db import models


class ExampleModel(models.Model):
    _list = models.TextField(default='[]')

    @property
    def list(self):
        return json.loads(self._list)

    @list.setter
    def list(self, value):
        self._list = json.dumps(self.list + value)

1

Sử dụng mối quan hệ một-nhiều (FK từ lớp Bạn đến lớp cha) sẽ giúp ứng dụng của bạn có khả năng mở rộng hơn (vì bạn có thể mở rộng đối tượng Bạn bè một cách tầm thường với các thuộc tính bổ sung ngoài tên đơn giản). Và do đó, đây là cách tốt nhất


3
Đó không phải là khả năng mở rộng, đó là khả năng mở rộng. Thường thì cái này có giá của cái kia. Trong trường hợp này, nếu bạn biết rằng bạn sẽ luôn cần một danh sách các chuỗi, bạn có thể tránh một phép nối đắt tiền, do đó làm cho mã của bạn có khả năng mở rộng hơn (nghĩa là hiệu suất cao hơn từ việc không chuẩn hóa).
Dustin Rasener

Ở trên với một vài lưu ý: 1) bạn biết rằng bạn không bao giờ muốn truy vấn dữ liệu đó và 2) lưu trữ vẫn rẻ hơn sức mạnh xử lý và bộ nhớ (ai biết, có thể điều này thay đổi với điện toán lượng tử)
Dustin Rasener
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.