web-dev-qa-db-ja.com

Djangoモデルにリストを保存する最も効率的な方法は何ですか?

現在、次のようなコードにpythonオブジェクトがたくさんあります。

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

次に、これをDjangoモデルに変換します。self.myNameは文字列フィールドで、self.myFriendsは文字列のリストです。

from Django.db import models

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

リストはpythonのこのような一般的なデータ構造であるため、そこにはDjangoモデルフィールドがあると予想されます。 ManyToMany関係またはOneToMany関係を使用できることは知っていますが、コード内の余分な間接参照を避けたいと思っていました。

編集:

私はこれを追加しました 関連する質問 、人々が役に立つと思うかもしれません。

119
grieve

この関係は、Friendsテーブルに対する1対多の外部キー関係としてより適切に表現されないでしょうか? myFriendsは単なる文字列であることは理解していますが、Friendモデルを作成し、MyClassに結果のテーブルへの外部キーの関係を含めることをお勧めします。

61
Andrew Hare

「早期最適化はすべての悪の根源です。」

それをしっかりと念頭に置いて、これをやってみましょう!アプリが特定のポイントに達すると、データの非正規化は非常に一般的です。正しく行うと、ハウスキーピングが少し増えますが、多数の高価なデータベース検索を保存できます。

友人名のlistを返すには、アクセス時にリストを返すカスタムDjango Fieldクラスを作成する必要があります。

David Cramerが彼のブログにSeperatedValueFieldを作成するためのガイドを投稿しました。コードは次のとおりです。

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)

このコードのロジックは、データベースからPythonへの値のシリアル化と逆シリアル化、およびその逆を扱います。モデルクラスのカスタムフィールドを簡単にインポートして使用できるようになりました。

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

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

リストをDjangoに保存する簡単な方法は、リストをJSON文字列に変換し、それをモデルにテキストとして保存することです。その後、(JSON)文字列をpythonリストに戻すことにより、リストを取得できます。方法は次のとおりです。

「リスト」はDjangoモデルに次のように保存されます。

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

ビュー/コントローラーコードで:

データベースへのリストの保存:

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()

データベースからリストを取得する:

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

概念的に、ここで何が起こっているのか:

>>> 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'>
32
mindthief

PostgresでDjango> = 1.9を使用している場合は、 ArrayField の利点を利用できます

データのリストを保存するためのフィールド。ほとんどのフィールドタイプを使用できます。別のフィールドインスタンスをbase_fieldとして渡すだけです。サイズを指定することもできます。 ArrayFieldをネストして、多次元配列を格納できます。

配列フィールドをネストすることもできます。

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,
    )

@ thane-brimhallが述べたように、要素を直接クエリすることもできます。ドキュメント 参照

17
wolendranh

これは古い質問であり、Djangoの手法は大幅に変更されているはずなので、この回答はDjangoバージョン1.4を反映しており、v 1.5に適用される可能性が最も高いです。

Djangoはデフォルトでリレーショナルデータベースを使用します。あなたはそれらを利用すべきです。 ManyToManyFieldを使用して、友情をデータベース関係(外部キー制約)にマップします。これにより、スマートクエリセットを使用するフレンドリストにRelatedManagerを使用できます。 filtervalues_listなどの使用可能なすべてのメソッドを使用できます。

ManyToManyFieldリレーションとプロパティの使用:

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())

この方法でユーザーの友達リストにアクセスできます。

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

ただし、これらの関係は対称的です。ジョセフがボブの友人である場合、ボブはジョセフの友人です。

15
sleblanc
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')
9
Andriy Drozdyuk

Postgresを使用している場合は、次のようなものを使用できます。

class ChessBoard(models.Model):

    board = ArrayField(
        ArrayField(
            models.CharField(max_length=10, blank=True),
            size=8,
        ),
        size=8,
    )

詳細が必要な場合は、以下のリンクで読むことができます: https://docs.djangoproject.com/pt-br/1.9/ref/contrib/postgres/fields/

6
Marcos Souza

これは、最終的にはリレーショナルデータベースにする必要があることに注意してください。したがって、リレーションを実際に使用すると、この問題を解決する一般的な方法isになります。オブジェクト自体にリストを保存することを絶対に主張する場合は、たとえばコンマ区切りでリストを作成し、文字列に保存してから、文字列をリストに分割するアクセサー関数を提供できます。それにより、文字列の最大数に制限され、効率的なクエリが失われます。

6

このスニペットのように、Django Pickle Fieldを使用して、事実上すべてのオブジェクトを保存できます。

http://www.djangosnippets.org/snippets/513/

4
Harold

Django model:に文字列のリストを保存する

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

次のように呼び出すことができます。

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."      
3
Ahtisham

1対多のリレーション(Friendから親クラスへのFK)を使用すると、アプリのスケーラビリティが向上します(単純な名前以外の追加属性でFriendオブジェクトを簡単に拡張できるため)。したがって、これが最良の方法です

1
Guard