Django 모델에 목록을 저장하는 가장 효율적인 방법은 무엇입니까?


146

현재 내 코드에는 다음과 유사한 많은 파이썬 객체가 있습니다.

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

이제 이것을 장고 모델로 바꾸고 싶습니다. 여기서 self.myName은 문자열 필드이고 self.myFriends는 문자열 목록입니다.

from django.db import models

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

목록은 파이썬에서 일반적인 데이터 구조이기 때문에 장고 모델 필드가있을 것으로 예상됩니다. 나는 ManyToMany 또는 OneToMany 관계를 사용할 수 있다는 것을 알고 있지만 코드에서 추가 간접적 인 것을 피하고 싶었습니다.

편집하다:

사람들이 유용하다고 생각하는 관련 질문을 추가했습니다 .


1
@drozzy : 아마도 다른 문구를 사용했을 수도 있지만 기본적으로 문자열 목록을 전달하고 문자열 목록을 다시 얻고 싶었습니다. 나는 많은 Friend 객체를 만들고 싶지 않고 각각에 대해 inst.myFriends.add (friendObj)를 호출하고 싶습니다. 그렇게 힘들지는 않겠지 만 ...
슬픔

답변:


77

이 관계가 Friends테이블 에 대한 일대 다 외래 키 관계로 더 잘 표현되지 않습니까? 나는 myFriends단지 문자열 이라는 것을 이해 하지만 더 나은 디자인은 Friend모델 을 만들고 MyClass결과 테이블에 대한 외래 키 구현 을 포함하는 것이라고 생각 합니다.


15
이것은 아마도 내가 끝내게 될 일이지만, 이것이 실제로 기본 구조가 내장되기를 바랐습니다. 나는 게으르다 고 생각합니다.
슬프다

우아하고 가장 아름답게 설명되었습니다.
Tessaracter


129

"조기 최적화는 모든 악의 근원입니다."

이를 확실하게 염두에두고이 작업을 수행하십시오! 앱이 특정 지점에 도달하면 데이터의 비정규 화가 매우 일반적입니다. 올바르게 수행하면 약간의 유지 관리 비용으로 많은 고가의 데이터베이스 조회를 절약 할 수 있습니다.

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

8
좋은 답변을 얻으려면 +1하지만 이미 이와 같은 일을하고 있습니다. 실제로 모든 값을 하나의 문자열로 쪼개고 나눕니다. 실제로 별도의 테이블을 작성하고 외래 키를 자동으로 만드는 ListofStringsField와 같은 것을 원한다고 생각합니다. 장고에서 가능한지 확실하지 않습니다. 그렇다면 대답을 찾으면 stackoverflow에 게시 할 것입니다.
슬프다

2
이 경우 initcrash의 django-denorm을 찾고 있습니다. github에서 찾을 수 있습니다 : github.com/initcrash/django-denorm/tree/master
jb.

3
+1. 그러나 문자열에서 쉼표로 가능한 문제. json에서 직렬화 및 역 직렬화는 어떻습니까?
sbeliakov 2016 년

이것을 기존 모델에 추가하려고 my_vals = SeparatedValuesField(blank=True, default="")하지만 NULL로 인해 IntegrityError가 발생합니다. 기본 인수가 올바르게 전달되지 않습니까?
John Lehmann

1
Django 2.1 to_python에서는 더 이상 읽을 때 호출되지 않습니다. 따라서이 작업을하려면 다음을 추가해야합니다. def from_db_value(self, value, expression, connection, context): return self.to_python(value)
theadriangreen

46

장고에 목록을 저장하는 간단한 방법은 목록을 JSON 문자열로 변환 한 다음 모델에서 텍스트로 저장하는 것입니다. 그런 다음 (JSON) 문자열을 다시 파이썬 목록으로 변환하여 목록을 검색 할 수 있습니다. 방법은 다음과 같습니다.

"목록"은 다음과 같이 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'>

8
불행히도 이것은 django admin을 사용하여 목록을 관리하는 데 도움이되지 않습니다
GreenAsJade

25

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이 언급했듯이 요소를 직접 쿼리하는 것도 가능합니다. 문서 참조


2
이것의 가장 큰 장점은 배열 필드에서 직접 요소를 쿼리 할 수 ​​있다는 것입니다.
Thane Brimhall

@ThaneBrimhall 당신이 맞아요. 이 정보로 답변을 업데이트해야 할 수도 있습니다. 감사합니다
wolendranh

슬프게도, mysql에 대한 해결책이 없습니다
Joel G Mathew

이것은 PostGres에서만 작동한다고 언급해야합니다.
theadriangreen

1
Django 1.8에는 ArrayField도 있습니다. docs.djangoproject.com/en/1.8/ref/contrib/postgres/fields
kontextify

15

이것은 오래된 질문이므로 Django 기술은 그 이후로 크게 바뀌었을 것입니다.이 답변은 Django 버전 1.4를 반영하며 v 1.5에 가장 적합합니다.

Django는 기본적으로 관계형 데이터베이스를 사용합니다. 당신은 그들을 사용해야합니다. ManyToManyField를 사용하여 데이터베이스 관계 (외부 키 제한 조건)에 우정을 맵핑하십시오. 그렇게하면 스마트 쿼리 셋을 사용하는 프렌드리스트에 RelatedManagers를 사용할 수 있습니다. 당신은 다음과 같은 가능한 모든 방법을 사용할 수 있습니다 filter또는 values_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

그러나 이러한 관계는 대칭 적입니다. Joseph이 Bob의 친구 인 경우 Bob은 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

이것은 결국 관계형 데이터베이스에서 끝나야합니다. 관계를 사용 그래서 정말 입니다 이 문제를 해결하는 일반적인 방법. 객체 자체에 목록을 저장해야한다고 주장하는 경우 목록을 쉼표로 구분하여 문자열로 저장 한 다음 문자열을 목록으로 나누는 접근 자 기능을 제공 할 수 있습니다. 이를 통해 최대 문자열 수로 제한되며 효율적인 쿼리가 손실됩니다.


3
나는 데이터베이스를 관계로 저장하는 것이 좋으며, 장고 모델이 이미 그 부분을 추상화하기를 바랐습니다. 앱 쪽에서 나는 항상 문자열 목록으로 취급하려고합니다.
슬프다



3

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

그리고 당신은 이것을 다음과 같이 부를 수 있습니다 :

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

1

일대 다 관계 (친구에서 부모 클래스로의 FK)를 사용하면 앱의 확장 성을 높일 수 있습니다 (단순한 이름 이상의 추가 속성으로 친구 개체를 간단하게 확장 할 수 있음). 따라서 이것이 가장 좋은 방법입니다


3
그것은 확장 성이 아니라 확장 성입니다. 종종 하나는 다른 하나를 희생합니다. 이 경우 항상 문자열 목록이 필요하다는 것을 알고 있으면 값 비싼 조인을 피할 수 있으므로 코드 확장 성을 높일 수 있습니다 (즉, 비정규 화에서 더 성능이 우수함).
Dustin Rasener

주의의 몇 위 : 1) 당신은 데이터, 2) 저장에 대한 질의에 싶지 않을 알고는 여전히 전력 및 메모리 (양자 컴퓨팅과, 어쩌면이 변경)을 알고 자 처리보다 저렴
더스틴 Rasener

1

내 솔루션은 누군가를 도울 수 있습니다.

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)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.