Django proxy model and ForeignKey

前端 未结 6 2060
温柔的废话
温柔的废话 2020-12-05 01:28

How to make entry.category to be instance of CategoryProxy? See code for details:

class Category(models.Model): pass

class Entry(models.Model):
    category         


        
相关标签:
6条回答
  • 2020-12-05 01:30

    This question already has an accepted answer, but wanted to post this for anyone who may come searching.

    You can patch the model at runtime with the new field so that relations work as expected. A full example can be seen here - https://gist.github.com/carymrobbins/8721082

    from django.db.models.fields.related import ReverseSingleRelatedObjectDescriptor
    
    def override_model_field(model, field, field_name, column_name):
        """Force override a field in a Django Model.
        Usage: override_model_field(
            MyModel, models.ForeignKey(OtherModel), 'other', 'other_id')
        :type model: django.db.models.base.ModelBase
        :type field: django.db.models.fields.Field
        :type field_name: basestring
        :type column_name: basestring
        """
        field.name = field_name
        field.attname = column_name
        for i, f in enumerate(model._meta.fields):
            if f.name == field_name:
                model._meta.fields[i] = field
                break
        else:
            raise TypeError('Model {!r} does not have a field {!r}.'
                            .format(model, field_name))
        model.add_to_class(field_name,
                           ReverseSingleRelatedObjectDescriptor(field))
    
    0 讨论(0)
  • 2020-12-05 01:35

    This is an open Django issue: #10961 (Allow users to override forward and reverse relationships on proxy models with ForeignKey fields)

    You can work around it by resetting the fields in question after you define the proxy models:

    EntryProxy.add_to_class('category', CategoryProxy)
    
    0 讨论(0)
  • 2020-12-05 01:39

    None of the current solutions (including the accepted one) work with Django 2.0.

    Building on Matt Schinckel's work on overriding proxy model relations, here's a solution that will work with Django 2.0 and 2.1.

    0 讨论(0)
  • 2020-12-05 01:44

    Adapting Bernd Petersohn's answer slightly, we then have:

    class EntryProxy(Entry):
        @property
        def category(self):
            return CategoryProxy.objects.get(id=self.category_id)
    

    This ought to be more economical with the database. For added improvements you could set a private attribute (self._category) the first time the method is called, then return that all subsequent times.

    0 讨论(0)
  • 2020-12-05 01:51

    To switch from a model class to a proxy class without hitting the database:

    class EntryProxy(Entry):
        @property
        def category(self):
            new_inst = EntryProxy()
            new_inst.__dict__ = super(EntryProxy, self).category.__dict__
            return new_inst
    

    edit: the snippet above seems not working on django 1.4.

    Since django 1.4, I take all value fields manually like this:

    class EntryProxy(Entry):
        @property
        def category(self):
            category = super(EntryProxy, self).category
            new_inst = EntryProxy()
            for attr in [f.attname for f in category.__class__._meta.fields] + ['_state']:
                setattr(new_inst, attr, getattr(category, attr))
            return new_inst
    

    To switch from a queryset to a child proxy class without hitting database:

    class CategoryProxy(Category):
        @property
        def entry_set(self):
            qs = super(CategoryProxy, self).entry_set
            qs.model = EntryProxy
            return qs
    
    0 讨论(0)
  • 2020-12-05 01:52

    Define a property category on EntryProxy that looks up the CategoryProxy by its id:

    class EntryProxy(Entry):
        @property
        def category(self):
            cid = super(EntryProxy, self).category.id
            return CategoryProxy.objects.get(id=cid)
    
        class Meta:
            proxy = True
    
    0 讨论(0)
提交回复
热议问题