Can I declare many-to-many through an intermediate model on both linked models, so I can take advantage of the automatic ModelForm?

This is my code:

class Resource(models.Model):
    [...]

class Theme(models.Model):
    [...]
    resource_set = models.ManyToManyField(Resource, through='Match', related_name='theme_set', blank=True)

class Match(models.Model):
    resource = models.ForeignKey(Resource)
    theme = models.ForeignKey(Theme)
    [...]

      

I am deliberately using the intermediate model because I want to add some attributes to the relationship. Now ... I know that when I declare a many-to-many relationship in Topic, I also get feedback from the Resource. See the Related Name I'm using? This way I have "symmetric" field names in both models (resource_set, theme_set and match_set in both models).

My problem is when creating forms from both models (Resource and Theme) they are not symmetrical. When I create a theme, I automatically get a multiple choice box to select from pre-existing resources. But when I create the resource form, I don't.

So I would like to declare a many-to-many relationship in both models - make them truly equal (not like in the Pizzas and Toppings example;) and create these additional multiple choice fields, The question is, is this possible, or do I need add multiple select field to ResourceForm myself or use FormSets which I haven't figured out yet?

BTW - since I can't save the new theme from the ThemeForm using save () ("Can't set values ​​on the ManyToManyField that specifies the intermediate model.") I do this:

[...]
theme = form.save(commit=False)
theme.save()
for resource in form.cleaned_data['resource_set']:
    match = Match()
    match.resource = resource
    match.theme = theme
    match.save()
return redirect([...]

      

+3


source to share


1 answer


You can simply define it on both sides.

One problem would be associated_name - As far as I know, there is no way to tell the ORM "I'll take care of the reverse relationship myself." Therefore, you will need to change the associated names so that they do not clash with the names of the real fields. In this example, I added an underscore to the associated_name, so the resource objects now have both theme_set and _theme_set, which are two descriptors for the same data.



class Resource(models.Model):
    [...]
    theme_set = models.ManyToManyField('Theme',
        through='Match', 
        related_name='_resource_set', 
        blank=True)

class Theme(models.Model):
    [...]
    resource_set = models.ManyToManyField(Resource,
        through='Match',
        related_name='_theme_set',
        blank=True)

      

Also, if you are using South, you might need add_ignored_fields to one of the fields.

0


source







All Articles