Добавление переменной Django Form без ввода поля

Я пытаюсь добавить переменную в форму Django, при этом переменная не должна быть полем формы. Мне нужен набор флажков, которые будут отмечены, если пользователь подписан на рассылку. Когда форма загружается, опции не загружаются успешно. При загрузке формы все подписки отмечаются. Функция обновления работает должным образом, и в базе данных отражаются правильные варианты после отправки. Пожалуйста, помогите мне выяснить, почему при загрузке формы не проверяются соответствующие подписки пользователей.

models.py

class Subscription(models.Model):
    name = models.CharField(max_length=100, null=False, blank=False)
    description = models.TextField(null=False, blank=False)

    def __str__(self):
        return self.name
    
class UserSubscription(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    subscription = models.ForeignKey(Subscription, on_delete=models.CASCADE)
    start_date = models.DateTimeField(auto_now_add=True)
    end_date = models.DateTimeField(null=True, blank=True)
    
    def __str__(self):
        return self.user.username + " " + self.subscription.name

views.py

@login_required(login_url='login')
def managesubscription(request):
    userSubscriptions = UserSubscription.objects.filter(user=request.user)
    subscriptions = Subscription.objects.all()
    if request.method == 'POST':
        form = ManageSubscriptionForm(request.POST, userSubscriptions=userSubscriptions)
        if form.is_valid():
            subscriptions = form.cleaned_data['subscriptions']
            UserSubscription.objects.filter(user=request.user).delete()
            for subscription in subscriptions:
                print(subscription)
                UserSubscription.objects.create(user=request.user, subscription=subscription)
            messages.info(request, 'Subscriptions Updated')
            return redirect('dashboard')
    form = ManageSubscriptionForm(userSubscriptions=userSubscriptions, initial={'subscriptions': subscriptions, 'userSubscriptions': userSubscriptions})
    return render(request, 'account/managesubscriptions.html', {'form': form})

forms.py

class ManageSubscriptionForm(forms.ModelForm):
    subscriptions = forms.ModelMultipleChoiceField(
        queryset=Subscription.objects.all(),
        widget=forms.CheckboxSelectMultiple(attrs={'class': 'form-check-input'}),   
    )

    def __init__(self, *args, userSubscriptions, **kwargs):
        self.userSubscriptions = userSubscriptions
        print("here", userSubscriptions)
        super().__init__(*args, **kwargs)
    class Meta:
        model = Subscription
        fields = ['subscriptions']

managesubscriptions.html

<body>
    <br>
    <div class="container bg-white shadow-md p-5 form-layout">
        <h3>Update Subscriptions</h3>
        {% csrf_token %}

        <br><br>
        <form method="POST" autocomplete="off">
            {% csrf_token %}
            {% for subscription in form.subscriptions %}
                <div class="form-check">
                    <input class="form-check-input" type="checkbox" name="subscriptions" value="{{ subscription.id }}" id="subscription{{ subscription.id }}" 
                    {% if subscription.id in userSubscriptions %} checked {% endif %}> {{ subscription }}
                </div>
            {% endfor %}
            <br />
            <button type="submit" class="btn btn-primary my-2">Update Subscriptions</button>
        </form>
    </div>

</body>

для начала нужно отфильтровать user_subscriptions, получив только те подписки, для которых пользователь уже зарегистрирован

 usersubscriptions = UserSubscription.objects.filter(user=request.user).values_list('subscription_id', flat=True)

values_list - это метод Django, который позволяет получить из набора запросов только определенные поля, а не весь объект. Он извлекает только поле subscription_id из каждого UserSubscription, найденного предыдущим фильтром. flat=True указывает, что результат должен быть простым списком значений, а не списком кортежей.

Без flat=True результатом будут кортежи, содержащие по одному элементу (например, [(1,), (2,), (3,)]).

При flat=True вы получите плоский список типа [1, 2, 3]. Извлекает подписки, выбранные в форме

selected_subscriptions = form.cleaned_data['subscriptions']
selected_subscription_ids = set(sub.id for sub in selected_subscriptions)

selected_subscription_ids создает набор, содержащий уникальные идентификаторы подписок, выбранных пользователем.

sub.id for sub in selected_subscriptions происходит из Python. По сути, он сканирует каждый объект Subscription в selected_subscriptions и извлекает его атрибут id. et le set позволяет преобразовать последовательность идентификаторов в ансамбль

Только удаление подписок, с которых снят флажок

UserSubscription.objects.filter(user=request.user).exclude(subscription_id__in=selected_subscription_ids).delete()

Определяет первоначально выбранные подписки

form = ManageSubscriptionForm(userSubscriptions=userSubscriptions, initial={'subscriptions': usersubscriptions})

в файле forms.py Для метода init:

def __init__(self, *args, subscriptions=None, **kwargs):
    super().__init__(*args, **kwargs)
    if subscriptions is not None:
        self.fields['subscriptions'].queryset = subscriptions

super().__init__(*args, **kwargs) вызывает конструктор родительского класса (forms.ModelForm). При этом форма инициализируется обычным образом, обрабатывая все аргументы и ключевые слова, переданные при создании экземпляра формы.

subscriptions позволит передать определенный набор запросов для поля подписки при инициализации.

if subscriptions is not None, т.е. если указано subscriptions, то кверисет self.fields['subscriptions'] заменяется этим конкретным кверисетом. Это означает, что в форме будут отображаться только экземпляры подписки, указанные в этом наборе кверисетов.

Вернуться на верх