Add Variable Django Form without Putting in Field
I am trying to add a variable to a Django form, without the variable being a field on the form. I want a set of checkboxes that are checked if the user is subscribed. When the form is loaded, the options are not loaded successfully. All subscriptions are checked when the form loads. The update function works appropriately and the database reflects the correct selections after submission. Please help me figure out why the appropriate usersubscriptions are not checked when the form loads.
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>
to start with, you need to filter user_subscriptions by retrieving only the subscriptions for which the user is already registered
usersubscriptions = UserSubscription.objects.filter(user=request.user).values_list('subscription_id', flat=True)
values_list
is a Django method that allows you to retrieve only certain fields from a queryset, rather than the whole object. It retrieves only the subscription_id field of each UserSubscription found by the previous filter.
flat=True
indicates that the result must be a simple list of values, not a list of tuples.
Without flat=True
, the result would be tuples containing a single element each (e.g. [(1,), (2,), (3,)]).
With flat=True
, you get a flat list like [1, 2, 3]. Retrieves the subscriptions selected in the form
selected_subscriptions = form.cleaned_data['subscriptions']
selected_subscription_ids = set(sub.id for sub in selected_subscriptions)
selected_subscription_ids
creates a set containing the unique IDs of the subscriptions selected by the user.
sub.id for sub in selected_subscriptions
comes from Python. Basically, it scans each Subscription object in selected_subscriptions and retrieves its id attribute.
et le set
permet de transformer la séquence d’IDs en un ensemble
Only deletes subscriptions that have been unchecked
UserSubscription.objects.filter(user=request.user).exclude(subscription_id__in=selected_subscription_ids).delete()
Defines the subscriptions initially selected
form = ManageSubscriptionForm(userSubscriptions=userSubscriptions, initial={'subscriptions': usersubscriptions})
in forms.py For the init method:
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)
calls the constructor of the parent class (forms.ModelForm). This initializes the form normally, processing all the arguments and keywords passed when the form instance was created.
subscriptions
will allow a specific queryset to be passed for the subscriptions field during initialisation.
if subscriptions is not None
i.e. if subscriptions
is provided, the queryset of self.fields['subscriptions']
is replaced by this specific queryset. This means that only Subscription instances specified in this queryset will be displayed in the form.