寻找一个Django自定义字段,通过在列中存储一个ID列表来模拟ManyToManyField

问题描述:

我想使用多对多关系将MyModel链接到AnotherModel,而不用实际使用SQL关系:相反,我想存储MyModel的列中的AnotherModel pks的列表,并且具有自定义的Field处理向QuerySet(或实例列表)的转换以及从AnotherModel到MyModel的反向关系。寻找一个Django自定义字段,通过在列中存储一个ID列表来模拟ManyToManyField

您是否知道有人已经完成了这项工作,或者您有任何建议以简单的方式完成?我已经开始自己实现它了,但是我开始意识到完全实现ManyToManyField的行为将会是多么的复杂:我对Django还是比较新的,并且正确地做这件事需要熟悉一下内部工作原理该框架。

到目前为止,我有这样的:

class InlineManyToManyField(models.CharField): 
    __metaclass__ = models.SubfieldBase 

    def __init__(self, other_model, *args, **kwargs): 
     try: 
      assert not other_model._meta.abstract, "{0} cannot define a relation with abstract class {0}".format(
       self.__class__.__name__, to._meta.object_name) 
     except AttributeError: 
      assert isinstance(other_model, basestring), "{0}({1}) is invalid. First parameter to InlineManyToManyField must be either a model, a model name, or the string self".format(
       self.__class__.__name__,unicode(other_model)) 
     kwargs['max_length'] = kwargs.get('max_length', 255) 
     kwargs['blank'] = kwargs.get('blank', True) 
     self.other_model = other_model 
     self.token = kwargs.pop('token', ' ') 
     super(InlineManyToManyField, self).__init__(*args, **kwargs) 

    def to_python(self, value): 
     if not value: return 
     if isinstance(value, basestring): 
      pk_list = value.split(self.token) 
      pk_list.pop(0) 
      pk_list.pop() 
      value = self.other_model._default_manager.filter(pk__in=pk_list) 
     return value 

    def get_db_prep_value(self, value, connection, prepared=False): 
     if not value: return 
     pk_list = [item.pk for item in value] 
     pk_list.sort() 
     return self.token + self.token.join(unicode(pk) for pk in pk_list) + self.token 

    def contribute_to_class(self, cls, name): 
     super(InlineManyToManyField, self).contribute_to_class(cls, name) 
     if isinstance(self.other_model, basestring): 
      def resolve_through_model(field, model, cls): 
       field.other_model = model 
      add_lazy_relation(cls, self, self.other_model, resolve_through_model) 

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

    def get_db_prep_lookup(self, lookup_type, value, connection, prepared=False): 
     if lookup_type in ('contains', 'icontains'): 
      if isinstance(value, self.other_model): 
       value = value.pk 
      return ["%{0}{1}{0}%".format(self.token, connection.ops.prep_for_like_query(value))] 
     return super(InlineManyToManyField, self).get_db_prep_lookup(
      lookup_type, value, connection=connection, prepared=prepared) 

这里是我如何使用它:

class MyModel(models.Model): 
    anotherlist = InlineManyToManyField(AnotherModel, token=':') 

如果为MyModel表包含PK = 1和anotherlist =“一行: 1:2:3:”我能做到这一点:

>>> m = MyModel.objects.get(pk=1) 
>>> m.anotherlist 
[<AnotherModel: 1>, <AnotherModel: 2>, <AnotherModel: 3>] 
>>> MyModel.objects.filter(anotherlist__contains=2) 
[<MyModel: 2>] 

我想什么未来补充的是相反的关系:我很想对AnotherModel我一个mymodel_set例如,使用上面的“包含”代码,但我很难理解 如何在django/db/models/fields/related.py中工作:)

因此,在我花了几天的时间来研究它之前,你是否偶然发现过任何类似的东西,或者你是否已经自己写过类似的东西?

+0

只是好奇;你为什么想这样做? –

+0

我有一个交易卡游戏。假设我有50万用户,每个用户平均拥有20个平台。每张卡片包含约90张卡片。使用一个常规的ManyToManyField,我的关系表将会有近十亿个条目......这太过于夸张了,我宁愿每个套牌都有一行,并在VARCHAR中“内联”列表。 – Skoot

+0

我想这很好,只要你不打算通过卡片搜索玩家(即哪些玩家拥有或使用给定的卡)。就个人而言,我觉得数据就是数据;无论它存储在单个字段还是作为一组行,它的数量都是相同的。 –

嗯......为什么?和..不要! 所有流行的sql数据库的规模相当不错。你不应该害怕内心的加入!他们没事!

你想要做的是有点疼痛...抛开一边,如果你删除AnotherModel实例,你会怎么做?扫描所有MyModel表的字符串ID? Django 5.6.6.1发布时会发生什么,并且某些内容不兼容?改写?还请考虑一下管理模块和含义/变化!

从一开始就不要过分承认最微不足道的表现!如果损坏修复,测试是不是!

在应用程序的体系结构中投入更多时间,并且您会好起来的!

回答你的问题:不,我也想那样的东西,搜索它,但没有发现任何有用的东西!

您无法按照自己喜欢的方式进行反转。相反,你只需要添加属性功能AnotherModel:

class AnotherModel(models.Model): 
    // set up model here 

    def mymodels(self): 
     return MyModel.objects.filter(anotherlist__contains=self.pk) 

这应该工作。