Skip to content

Fixed: #12203 ManyToManyField with explicit through model can't be used ...#2843

Closed
vin-ai wants to merge 10 commits intodjango:masterfrom
vin-ai:master
Closed

Fixed: #12203 ManyToManyField with explicit through model can't be used ...#2843
vin-ai wants to merge 10 commits intodjango:masterfrom
vin-ai:master

Conversation

@vin-ai
Copy link
Copy Markdown

@vin-ai vin-ai commented Jun 23, 2014

...as an admin field

If user has defined through fields then return with no error

VINAY Kr. SHARMA added 2 commits June 23, 2014 14:38
Fixed: #12203 ManyToManyField with explicit through model can't be used as an admin field
@coder9042
Copy link
Copy Markdown
Contributor

@Vinaykrsharma
The patch would require tests.

@vin-ai
Copy link
Copy Markdown
Author

vin-ai commented Jun 23, 2014

Added test

Guys I'm not expert in testing and documentation so, please help me to writing test and doc

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use the @python_2_unicode_compatible technique that the other models use, although I don't think this change is needed for the tests.

@timgraham
Copy link
Copy Markdown
Member

Hi, you may be interested in our patch review checklist. Let me know if you have any questions as to how to update the patch and thanks for your efforts.

@vin-ai
Copy link
Copy Markdown
Author

vin-ai commented Jun 25, 2014

Also checked with flake8 no errors/warnings seen!

@timgraham
Copy link
Copy Markdown
Member

The widget display now, but can you actually use it? When I tried with the models in this example, I get an error when I try to save a Book: Cannot set values on a ManyToManyField which specifies an intermediary model. Use polls.AuthorsBooks's Manager instead.

By the way, this is what I meant about putting the models inline to the test: http://dpaste.com/1ZATEC8

@vin-ai
Copy link
Copy Markdown
Author

vin-ai commented Jun 25, 2014

I don't changed that because I thought model classes should be in models.py file.
Ok, now I'm going to implement your diff as on http://dpaste.com/1ZATEC8

…ts.py. Both model classes are testing purposes only.
@timgraham
Copy link
Copy Markdown
Member

And do you plan to respond to my question about whether the widget actually works? Not much point if removing the check if not.

@timgraham
Copy link
Copy Markdown
Member

Now when trying to add a Book via /admin/<app>/book/add/, I get IntegrityError at /admin/polls/book/add/ polls_authorsbooks.priority may not be NULL

@vin-ai
Copy link
Copy Markdown
Author

vin-ai commented Jun 26, 2014

Yes, because the M2M through model class don't know what value to save for that.
So, please add default value for field priority to prevent IntegrityError.

    # ...
    priority = models.IntegerField(default=0, blank=True)
    #...

or any best solution/patch do you have!

@vin-ai
Copy link
Copy Markdown
Author

vin-ai commented Jun 26, 2014

Also we should create a new method or property under field.rel object which will return value of self.field.rel.through._meta.auto_created or self.field.rel.through_fields is not None.

Because there are tons of checks for self.field.rel.through._meta.auto_created all over in Django framework. So, if we will add above condition every where then code size will increase and will look awkward.

Property to be add in field.rel object.

For e.g. let say:
field.rel.have_m2m_rel

try command line search:

$ grep '\.auto_created' -r ~/django-trunk/django

@timgraham
Copy link
Copy Markdown
Member

re: "Yes, because the M2M through model class don't know what value to save for that."

That's the point of the validation -- in general, we can't remove the check in this case. I believe the correct fix is described in this comment:

"The fancy solution would be to loosen this condition slightly and allow an m2m field to be in the fields list (and therefore allow an m2m widget be used) iff the through model is just 2 foreign keys with a unique_together pairing (i.e., if the through model is exactly the same as the m2m model that is be automatically generated for the non-explicit through case)."

@vin-ai
Copy link
Copy Markdown
Author

vin-ai commented Jun 27, 2014

@timgraham it's already done. I think nothing to do anything more.

How?

M2M Model having only two ForeignKey fields will use directly from fields attribute in ModelAdmin class. But if M2M Model having extra fields with two ForeignKey fields will go through Inlines classes.

Example: (try by creating a new app, put both separated code below to models.py and admin.py file)

# models.py
from django.db import models
from django.utils.encoding import python_2_unicode_compatible


@python_2_unicode_compatible
class Author(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name


@python_2_unicode_compatible
class Book(models.Model):
    title = models.CharField(max_length=120)
    cover_story = models.TextField(blank=True, null=True)
    authors = models.ManyToManyField(Author, through='AuthorsBooks', through_fields=('book', 'author'))

    def __str__(self):
        return self.title


@python_2_unicode_compatible
class AuthorsBooks(models.Model):
    author = models.ForeignKey(Author)
    book = models.ForeignKey(Book)
    priority = models.IntegerField()

    class Meta:
        verbose_name = 'Authors of Book'
        verbose_name_plural = 'Authors of Book'

    def __str__(self):
        return '%s by %s [%d]' % (self.book.title, self.author.name, self.priority)


# admin.py
from django.contrib import admin
from .models import Author, Book, AuthorsBooks


class AdminAuthorsBooksInline(admin.TabularInline):
    model = AuthorsBooks
    fields = ['book', 'priority']


class AdminAuthorsBooksInline2(admin.TabularInline):
    model = AuthorsBooks
    fields = ['author', 'priority']
    extra = 1

    class Meta:
        proxy = True


@admin.register(Author)
class AdminAuthor(admin.ModelAdmin):
    list_display = ['name']
    fields = ['name']
    inlines = [AdminAuthorsBooksInline]


# Both different example here
# try by changing True to False for below decision maker
if True:
    @admin.register(Book)
    class AdminBookInline(admin.ModelAdmin):
        """Book Admin with Inline example"""
        list_display = ['title', 'cover_story']
        fields = ['title', 'cover_story']
        inlines = [AdminAuthorsBooksInline2]

else:
    @admin.register(Book)
    class AdminBookSelectMultiple(admin.ModelAdmin):
        """Book Admin with SelectMultiple widget example"""
        list_display = ['title', 'cover_story']
        fields = ['title', 'cover_story', 'authors']

@timgraham
Copy link
Copy Markdown
Member

I'm not sure what you're saying. In your example code, AdminBookInline seems to work fine (and the current admin validation check doesn't prevent it from being used). However, AdminBookSelectMultiple (which currently throws the error <class 'test12203.admin.AdminBookSelectMultiple'>: (admin.E013) The value of 'fields' cannot include the ManyToManyField 'authors', because that field manually specifies a relationship model.) cannot be used because of the IntegrityError that's raised when trying to add a Book as we discussed before (there's no way in the UI to add a value for the priority field on the M2M model).

If the through model contains more than just the 2 FK fields, we should continue to throw an error because there isn't a way in the M2M widget to specify values of the other through fields. Does it make sense?

@vin-ai
Copy link
Copy Markdown
Author

vin-ai commented Jun 27, 2014

You should apply my commit in this pull request first. then try with that framework.

In short, download my patch which is in this pull request!

@timgraham
Copy link
Copy Markdown
Member

Yes, I did.

Before: Validation error with AdminBookSelectMultiple.

After: No validation error but IntegrityError when creating a new book in the admin.

Expected behavior: Validation error still occurs because AuthorsBooks is more than 2 FK fields (it contains an extra field "priority" that can't be set in the UI).

@vin-ai
Copy link
Copy Markdown
Author

vin-ai commented Jun 28, 2014

I'm bit confused why you're getting those type of error.
I've tested from every angel and not getting any errors.
Tell me how can I show you?

@timgraham
Copy link
Copy Markdown
Member

Steps to reproduce using your branch using the models/admins from your comment above (with AdminBookSelectMultiple):

Add a book: /admin/<app_label>/book/add/
Title: Foo
Cover Story: bar
Author: (select one)
click save

IntegrityError at /admin/test12203/book/add/
test12203_authorsbooks.priority may not be NULL

@vin-ai
Copy link
Copy Markdown
Author

vin-ai commented Jun 30, 2014

Ahh, missed one thing to mention in above example. So, sorry.

Just add default value to priority field. This will require if using SelectMultiple widget.

# models.py
class AuthorsBooks(models.Model):
    author = models.ForeignKey(Author)
    book = models.ForeignKey(Book)
    # This is extra field, if using SelectMultiple widget set a default value
    # to prevent IntegrityError, also allow blank.
    priority = models.IntegerField(default=0, blank=True)

@timgraham
Copy link
Copy Markdown
Member

As I tried to say above, the purpose of the validation is that, in general, when there are extra fields on the M2M model, there's no way to set them in the UI. We can't require users to put defaults on all the other fields just so this works. Once again, I'll restate the expected solution:

"The fancy solution would be to loosen this condition slightly and allow an m2m field to be in the fields list (and therefore allow an m2m widget be used) if and only if the through model is just 2 foreign keys with a unique_together pairing (i.e., if the through model is exactly the same as the m2m model that is be automatically generated for the non-explicit through case)."

@timgraham
Copy link
Copy Markdown
Member

Please send a new PR if you can fix it, thanks.

@timgraham timgraham closed this Jul 8, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants