Django - 如何通过用户先前选择的选项填充表单中的manytomany字段

All*_*ast 2 python forms django orm

如何使用先前用户选择的子项填充多对多表单字段。

在此代码中,表单呈现带有空复选框的选项。我想要复选框来显示用户订阅了哪些订阅。

模型.py

class Subscription(models.Model):
    SUBSCRIPTION_TYPES = (
        ('SUB1', _('sub 1')),
        ('SUB2', _('sub 2')),
    )

    subscription_type = models.CharField(choices=SUBSCRIPTION_TYPES, max_length=30, unique=True)
    description = models.CharField(max_length=255, blank=True)

class UserSubscription(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    subscriptions = models.ManyToManyField(Subscription, related_name='subscriptions',
                                           related_query_name='subscriptions')
Run Code Online (Sandbox Code Playgroud)

表格.py

class SubscriptionForm(forms.ModelForm):
    class Meta:
        model = UserSubscription
        fields = ('subscriptions',)
        widgets = {
            'subscriptions': forms.CheckboxSelectMultiple(),
        }
Run Code Online (Sandbox Code Playgroud)

视图.py

class SubscriptionFormView(FormView):
    template_name = 'profile/subscription.html'
    form_class = SubscriptionForm
Run Code Online (Sandbox Code Playgroud)

Wil*_*sem 9

请不要创建UserSubscription现在您定义了两个连接表。这将导致重复数据,并使查询效率降低,逻辑更容易出错。

你需要的是ManyToManyFieldSubscriptionUser,所以:

class Subscription(models.Model):
    # …
    subscribers = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        related_name='subscriptions'
    )
Run Code Online (Sandbox Code Playgroud)

然后我们可以定义一个表单来选择Subscriptions:

from django import forms

class SubscribingForm(forms.Form):
    subscriptions = forms.ModelMultipleChoiceField(
        queryset=Subscription.objects.all(),
        widget=forms.CheckboxSelectMultiple()
    )
Run Code Online (Sandbox Code Playgroud)

然后在视图中我们可以处理表单并将登录用户订阅所有已选择的订阅:

from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import redirect

class SubscriptionFormView(LoginRequiredMixin, FormView):
    template_name = 'profile/subscription.html'
    form_class = SubscribingForm
    
    def get_initial(self):
        initial = super().get_initial()
        initial['subscriptions'] = self.request.user.subscriptions.all()
        return initial
    
    def form_valid(self, form):
        subs = form.cleaned_data['subscriptions']
        self.request.user.subscriptions.add(*subs)
        return redirect('name-of-some-view')
Run Code Online (Sandbox Code Playgroud)

注意:您可以使用LoginRequiredMixinmixin [Django-doc]将视图限制为基于类的视图,以供经过身份验证的用户使用 。


注意:如果 POST 请求成功,您应该创建一个redirect [Django-doc] 来实现Post/Redirect/Get模式 [wiki]。这可以避免您在用户刷新浏览器时发出相同的 POST 请求。