How would you inherit from and override the django model classes to create a listOfStringsField?

后端 未结 5 1999
轻奢々
轻奢々 2021-02-06 05:24

I want to create a new type of field for django models that is basically a ListOfStrings. So in your model code you would have the following:

models.py:

5条回答
  •  北海茫月
    2021-02-06 05:47

    Thanks for all those that answered. Even if I didn't use your answer directly the examples and links got me going in the right direction.

    I am not sure if this is production ready, but it appears to be working in all my tests so far.

    class ListValueDescriptor(object):
    
       def __init__(self, lvd_parent, lvd_model_name, lvd_value_type, lvd_unique, **kwargs):
          """
             This descriptor object acts like a django field, but it will accept
             a list of values, instead a single value.
             For example:
                # define our model
                class Person(models.Model):
                   name = models.CharField(max_length=120)
                   friends = ListValueDescriptor("Person", "Friend", "CharField", True, max_length=120)
    
                # Later in the code we can do this
                p = Person("John")
                p.save() # we have to have an id
                p.friends = ["Jerry", "Jimmy", "Jamail"]
                ...
                p = Person.objects.get(name="John")
                friends = p.friends
                # and now friends is a list.
             lvd_parent - The name of our parent class
             lvd_model_name - The name of our new model
             lvd_value_type - The value type of the value in our new model
                            This has to be the name of one of the valid django
                            model field types such as 'CharField', 'FloatField',
                            or a valid custom field name.
             lvd_unique - Set this to true if you want the values in the list to
                         be unique in the table they are stored in. For
                         example if you are storing a list of strings and
                         the strings are always "foo", "bar", and "baz", your
                         data table would only have those three strings listed in
                         it in the database.
             kwargs - These are passed to the value field.
          """
          self.related_set_name = lvd_model_name.lower() + "_set"
          self.model_name = lvd_model_name
          self.parent = lvd_parent
          self.unique = lvd_unique
    
          # only set this to true if they have not already set it.
          # this helps speed up the searchs when unique is true.
          kwargs['db_index'] = kwargs.get('db_index', True)
    
          filter = ["lvd_parent", "lvd_model_name", "lvd_value_type", "lvd_unique"]
    
          evalStr = """class %s (models.Model):\n""" % (self.model_name)
          evalStr += """    value = models.%s(""" % (lvd_value_type)
          evalStr += self._params_from_kwargs(filter, **kwargs) 
          evalStr += ")\n"
          if self.unique:
             evalStr += """    parent = models.ManyToManyField('%s')\n""" % (self.parent)
          else:
             evalStr += """    parent = models.ForeignKey('%s')\n""" % (self.parent)
          evalStr += "\n"
          evalStr += """self.innerClass = %s\n""" % (self.model_name)
    
          print evalStr
    
          exec (evalStr) # build the inner class
    
       def __get__(self, instance, owner):
          value_set = instance.__getattribute__(self.related_set_name)
          l = []
          for x in value_set.all():
             l.append(x.value)
    
          return l
    
       def __set__(self, instance, values):
          value_set = instance.__getattribute__(self.related_set_name)
          for x in values:
             value_set.add(self._get_or_create_value(x))
    
       def __delete__(self, instance):
          pass # I should probably try and do something here.
    
    
       def _get_or_create_value(self, x):
          if self.unique:
             # Try and find an existing value
             try:
                return self.innerClass.objects.get(value=x)
             except django.core.exceptions.ObjectDoesNotExist:
                pass
    
          v = self.innerClass(value=x)
          v.save() # we have to save to create the id.
          return v
    
       def _params_from_kwargs(self, filter, **kwargs):
          """Given a dictionary of arguments, build a string which 
          represents it as a parameter list, and filter out any
          keywords in filter."""
          params = ""
          for key in kwargs:
             if key not in filter:
                value = kwargs[key]
                params += "%s=%s, " % (key, value.__repr__())
    
          return params[:-2] # chop off the last ', '
    
    class Person(models.Model):
       name = models.CharField(max_length=120)
       friends = ListValueDescriptor("Person", "Friend", "CharField", True, max_length=120)
    

    Ultimately I think this would still be better if it were pushed deeper into the django code and worked more like the ManyToManyField or the ForeignKey.

提交回复
热议问题