Django ModelMultipleChoiceField не сохраняется - PullRequest
0 голосов
/ 12 января 2019

Я получил поле ManyToMany с ModelMultipleChoiceField работающим администратором django:

Working ManyToMany Admin UI Element

Когда я добавляю один элемент с левой стороны к правой стороне и нажимаю «Сохранить» в пользовательском интерфейсе администратора, затем перезагружаю страницу сведений, где отображается этот ModelMultipleChoiceField, сохраненные элементы четко видны на правая сторона.

Код для этой рабочей модели выглядит следующим образом:

# project/account/models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from model_utils import Choices

class User(AbstractBaseUser, PermissionsMixin):
    following = models.ManyToManyField('self', related_name='followers', blank=True, symmetrical=False)
# project/account/forms.py
from django import forms
from django.contrib.auth import get_user_model
from django.contrib.admin.widgets import FilteredSelectMultiple
from .models import User

class UserChangeForm(forms.ModelForm):
    following = forms.ModelMultipleChoiceField(
        queryset=User.objects.all(),
        required=False,
        widget=FilteredSelectMultiple(
            verbose_name='Following',
            is_stacked=False
        )
    )
    followers = forms.ModelMultipleChoiceField(
        queryset=User.objects.all(),
        required=False,
        widget=FilteredSelectMultiple(
            verbose_name='Followers',
            is_stacked=False
        )
    )

    class Meta:
        model = get_user_model()
        fields = ('following', 'followers')

    def __init__(self, *args, **kwargs):
        super(UserChangeForm, self).__init__(*args, **kwargs)

        if self.instance and self.instance.pk:
            self.fields['following'] = forms.ModelMultipleChoiceField(
                queryset=User.objects.all().exclude(pk=self.instance.pk),
                required=False,
                widget=FilteredSelectMultiple(
                    verbose_name='Following',
                    is_stacked=False
                )
            )
            self.fields['followers'] = forms.ModelMultipleChoiceField(
                queryset=User.objects.all().exclude(pk=self.instance.pk),
                required=False,
                widget=FilteredSelectMultiple(
                    verbose_name='Followers',
                    is_stacked=False
                )
            )
            self.fields['followers'].initial = self.instance.followers.all()
# project/account/admin.py
from django.contrib.auth import get_user_model
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from .models import User
from .forms import UserChangeForm, UserCreationForm

class Admin(BaseUserAdmin):
    form = UserChangeForm
    model = get_user_model()

    fieldsets = (
        ('Following / Followers', {'fields': ('following', 'followers')}),
    )

admin.site.register(User, Admin)
admin.site.unregister(Group)

Это сохранит измененные представления в панели администратора, как и ожидалось.

Но теперь у меня есть другой вариант использования, где я хочу иметь три разных ModelMultipleChoiceField с для одной и той же модели:

# project/account/models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from model_utils import Choices
from shared_models.models import Badge

class User(AbstractBaseUser, PermissionsMixin):
    badges = models.ManyToManyField(Badge, related_name='owners', blank=True)
# project/shared_models/models.py
from django.db import models
from model_utils import Choices

class Badge(models.Model):
    BADGE_TYPES = Choices('bronze', 'silver', 'gold')

    type = models.CharField(choices=BADGE_TYPES, max_length=10)
    description = models.CharField(blank=False, max_length=1500)

    class Meta:
        app_label = 'shared_models'

    def __set__(self, instance, value):
        self.description = value
# project/account/forms.py
from django import forms
from django.contrib.auth import get_user_model
from django.contrib.admin.widgets import FilteredSelectMultiple
from shared_models.models import Badge
from .models import User

def general_model_multiple_choice_field_filter_type(model, verbose_name, filter_by_type):
    queryset = model.objects.all()
    if filter:
        queryset = queryset.filter(type=filter_by_type)
    return forms.ModelMultipleChoiceField(
        queryset=queryset,
        required=False,
        widget=FilteredSelectMultiple(
            verbose_name=verbose_name,
            is_stacked=False
        )
    )

class UserChangeForm(forms.ModelForm):
    bronze_badges = general_model_multiple_choice_field_filter_type(model=Badge, verbose_name='Bronze Badges', filter_by_type='bronze')
    silver_badges = general_model_multiple_choice_field_filter_type(model=Badge, verbose_name='Silver Badges', filter_by_type='silver')
    gold_badges = general_model_multiple_choice_field_filter_type(model=Badge, verbose_name='Gold Badges', filter_by_type='gold')

    class Meta:
        model = get_user_model()
        fields = ('bronze_badges',
                  'silver_badges',
                  'gold_badges')

    def __init__(self, *args, **kwargs):
        super(UserChangeForm, self).__init__(*args, **kwargs)

        if self.instance and self.instance.pk:
            self.fields['bronze_badges'] = general_model_multiple_choice_field_filter_type(Badge,
                                            'Bronze Badges',
                                            filter_by_type='bronze')
            self.fields['silver_badges'] = general_model_multiple_choice_field_filter_type(Badge,
                                            'Silver Badges',
                                            filter_by_type='silver')
            self.fields['gold_badges'] = general_model_multiple_choice_field_filter_type(Badge,
                                            'Gold Badges',
                                            filter_by_type='gold')
# project/account/admin.py
from django.contrib.auth import get_user_model
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from .models import User
from django.db.models import Q
from .forms import UserChangeForm, UserCreationForm

class Admin(BaseUserAdmin):
    form = UserChangeForm
    model = get_user_model()

    fieldsets = (
        ('Badges', {'fields': ('bronze_badges', 'silver_badges', 'gold_badges')}),
    )

admin.site.register(User, Admin)
admin.site.unregister(Group)

Но теперь, когда я пытаюсь добавить несколько значков в раздел «Выбранный» элемента пользовательского интерфейса, затем нажмите «Сохранить», перезагрузите страницу, выбранные элементы все еще остаются с левой стороны, а правая сторона остается пустой. Почтовый запрос ответил 302.

Not saving ModelMultipleChoiceField

Так как мне получить django для сохранения изменений в поле User.badges?

РЕДАКТИРОВАТЬ нашел вопрос, который отлично спрашивает, что мне нужно: Есть ли модель MultiField (любой способ составить БД модели Fields в Django)? Или почему это не будет полезным понятием?

...