django admin many-to-many intermediary models using through= and filter_horizontal

后端 未结 3 1863
悲哀的现实
悲哀的现实 2020-12-05 03:08

This is how my models look:

class QuestionTagM2M(models.Model):
    tag = models.ForeignKey(\'Tag\')
    question = models.ForeignKey(\'Question\')
    date_         


        
相关标签:
3条回答
  • 2020-12-05 03:35

    The docs may have changed since the previous answers were posted. I took a look at the django docs link that @Irfan mentioned and it seems to be a more straight forward then it used to be.

    Add an inline class to your admin.py and set the model to your M2M model

    class QuestionTagM2MInline(admin.TabularInline):
        model = QuestionTagM2M
        extra = 1
    

    set inlines in your admin class to contain the Inline you just defined

    class QuestionAdmin(admin.ModelAdmin):
        #...other stuff here
        inlines = (QuestionTagM2MInline,)
    

    Don't forget to register this admin class

    admin.site.register(Question, QuestionAdmin)
    

    After doing the above when I click on a question I have the form to do all the normal edits on it and below that are a list of the elements in my m2m relationship where I can add entries or edit existing ones.

    0 讨论(0)
  • 2020-12-05 03:45

    There are ways to do this

    • As provided by @obsoleter in the comment below : set QuestionTagM2M._meta.auto_created = True and deal w/ syncdb matters.
    • Dynamically add date_added field to the M2M model of Question model in models.py

      class Question(models.Model):
          # use auto-created M2M model
          tags = models.ManyToMany(Tag, related_name='questions')
      
      
      # add date_added field to the M2M model
      models.DateTimeField(auto_now_add=True).contribute_to_class(
               Question.tags.through, 'date_added')
      

      Then you could use it in admin as normal ManyToManyField.
      In Python shell, use Question.tags.through to refer the M2M model.

      Note, If you don't use South, then syncdb is enough; If you do, South does not like this way and will not freeze date_added field, you need to manually write migration to add/remove the corresponding column.

    • Customize ModelAdmin:

      1. Don't define fields inside customized ModelAdmin, only define filter_horizontal. This will bypass the field validation mentioned in Irfan's answer.
      2. Customize formfield_for_dbfield() or formfield_for_manytomany() to make Django admin to use widgets.FilteredSelectMultiple for the tags field.
      3. Customize save_related() method inside your ModelAdmin class, like

    def save_related(self, request, form, *args, **kwargs):
        tags = form.cleaned_data.pop('tags', ())
        question = form.instance
        for tag in tags:
            QuestionTagM2M.objects.create(tag=tag, question=question)
        super(QuestionAdmin, self).save_related(request, form, *args, **kwargs)
    
    • Also, you could patch __set__() of the ReverseManyRelatedObjectsDescriptor field descriptor of ManyToManyField for date_added to save M2M instance w/o raise exception.
    0 讨论(0)
  • 2020-12-05 03:58

    From https://docs.djangoproject.com/en/dev/ref/contrib/admin/#working-with-many-to-many-intermediary-models

    When you specify an intermediary model using the through argument to a ManyToManyField, the admin will not display a widget by default. This is because each instance of that intermediary model requires more information than could be displayed in a single widget, and the layout required for multiple widgets will vary depending on the intermediate model.

    However, you can try including the tags field explicitly by using fields = ('tags',) in admin. This will cause this validation exception

    'QuestionAdmin.fields' can't include the ManyToManyField field 'tags' because 'tags' manually specifies a 'through' model.

    This validation is implemented in https://github.com/django/django/blob/master/django/contrib/admin/validation.py#L256

            if isinstance(f, models.ManyToManyField) and not f.rel.through._meta.auto_created:
                raise ImproperlyConfigured("'%s.%s' "
                    "can't include the ManyToManyField field '%s' because "
                    "'%s' manually specifies a 'through' model." % (
                        cls.__name__, label, field, field))
    

    I don't think that you can bypass this validation unless you implement your own custom field to be used as ManyToManyField.

    0 讨论(0)
提交回复
热议问题