Django - Form-Select field set as not required not working - django

I have a form and want to make some fields not required.
It's working for most, but pairing_rum field (and a few others) are causing some difficulties. For this question I removed the other fields as they are in the same format as rum_pairing.
Here is what I tried so far:
using the same format as venue ie forms.ModelChoiceField(queryset = Venue.objects.all() - but how can I filter to only get the choices contained within the field of the model?
I also tried to modify the forms __init__, but no luck (example below - sorry for the formatting, stack is not letting me putting it in a code format)
def init(self, *args, **kwargs):
super(ReviewRating, self).init(*args, **kwargs)
self.fields['pairing_rum'].required = False
Which renders the following error:
super(type, obj): obj must be an instance or subtype of type
What would be the best way to make pairing_rum field not requried?
My current code is as follows:
models
class ReviewRating(models.Model):
user = models.ForeignKey(User,blank=True,null=True, on_delete=models.SET_NULL, related_name="usercomments") product=models.ForeignKey(Product,related_name="comments",null=True, on_delete=models.SET_NULL)
pairing_coffee = models.IntegerField(choices=COFFEE_PAIRING, default=0, blank=True, null=True)
pairing_rum = models.IntegerField(choices=RUM_PAIRING, default=0, blank=True, null=True)
pairing_gin = models.IntegerField(choices=GIN_PAIRING, default=0, blank=True, null=True)
pairing_whisky = models.IntegerField(choices=WHISKY_PAIRING, default=0, blank=True, null=True)
pairing_event = models.ForeignKey(CatalogueEvent, related_name="pairing_event",on_delete=models.SET_NULL,blank=True, null=True)
pairing_venue = models.ForeignKey(Catalogue, related_name="pairing_venue",on_delete=models.SET_NULL,blank=True, null=True)
review =models.TextField(max_length=250, blank=True)
rating =models.IntegerField(choices=RATING, default=0)
venue = models.ForeignKey(Venue, blank=True, null=True, related_name="venues", on_delete=models.SET_NULL)
forms
class ReviewForm(forms.ModelForm):
venue = forms.ModelChoiceField(queryset = Venue.objects.all(),required=False)
class Meta:
model = ReviewRating
fields = ['review', 'rating','venue','pairing_coffee', 'pairing_rum','pairing_gin','pairing_whisky', 'pairing_event','pairing_venue']
widgets = {
'venue': forms.TextInput(attrs={'class':'form-select', 'placeholder':'Enter name'}),
'pairing_coffee': forms.Select(attrs={'class':'form-select', 'placeholder':'Enter name'}),
'pairing_rum': forms.Select(attrs={'class':'form-select', 'placeholder':'Enter name'}),
'pairing_gin': forms.Select(attrs={'class':'form-select', 'placeholder':'Enter name'}),
'pairing_whisky': forms.Select(attrs={'class':'form-select', 'placeholder':'Enter name'}),
'pairing_event': forms.Select(attrs={'class':'form-select', 'placeholder':'Enter name'}),
'pairing_venue': forms.Select(attrs={'class':'form-select', 'placeholder':'Enter name'}),
}
views
def show_product(request, product_id):
url = request.META.get('HTTP_REFERER')
product = Product.objects.get(pk=product_id)
form = ReviewForm()
submitted = False
data = Product.objects.filter(pk=product_id).annotate(
avg_rating =Avg('comments__rating'),)
if request.method == "POST" and 'btnreview_form' in request.POST:
form = ReviewForm(request.POST)
if form.is_valid():
data = form.save(commit=False)
data.product_id = product_id
data.user_id = request.user.id
data.save()
form.save()
return redirect(url)
else:
print(form.errors)
else:
print(form.errors)
return render(request,"main/show_product.html", {'form':form, 'submitted':submitted, 'product':product, 'venue_form':venue_form, 'data':data})
template
{% if user.is_authenticated %}
<form action="{% url 'show-product' product.id%}" method="POST">
{% csrf_token %}
<div class="rate">
<input type="radio" name="rating" id="rating1" value="1" required /><label for="rating1" title="1"></label>
<input type="radio" name="rating" id="rating2" value="2" required /><label for="rating2" title="2"></label>
<input type="radio" name="rating" id="rating3" value="3" required /><label for="rating3" title="3"></label>
<input type="radio" name="rating" id="rating4" value="4" required /><label for="rating4" title="4"></label>
<input type="radio" name="rating" id="rating5" value="5" required /><label for="rating5" title="5"></label>
</div>
{%if product.category == 1 %}
{{ form.pairing_rum|as_crispy_field }}
{%endif%}
{%if product.category == 2 %}
{{ form.pairing_gin|as_crispy_field }}
{%endif%}
{%if product.category == 3 %}
{{ form.pairing_whisky|as_crispy_field }}
{%endif%}
{%if product.category == 8 %}
{{ form.pairing_coffee|as_crispy_field }}
{%endif%}
{{ form.review|as_crispy_field }}
{{ form.venue|as_crispy_field }}
{% comment %} <a class="btn btn-outline-secondary btn-sm" href ="" data-toggle="modal" data-target="#AddVenueFormModal" ><font size="3px" color="#008083">Can't find the venue?</font></a> {% endcomment %}
<input type="submit" name="btnreview_form" value="Submit Review" class="btn btn-primary custom-btn">
</form>
</br>
{% else %}
{% endif %}
EDITED to add Traceback
The traceback indicates there is a problem at form.save(commit=False)
[23/Dec/2022 13:53:39] "GET /show_product/109 HTTP/1.1" 200 15414
Internal Server Error: /show_product/109
Traceback (most recent call last):
File "C:\Users\CeX\Virtual\lib\site-packages\django\core\handlers\exception.py", line 55, in inner
response = get_response(request)
File "C:\Users\CeX\Virtual\lib\site-packages\django\core\handlers\base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\CeX\Dev\mysite\main\views.py", line 760, in show_product
data = form.save(commit=False)
File "C:\Users\CeX\Virtual\lib\site-packages\django\forms\models.py", line 539, in save
raise ValueError(
ValueError: The ReviewRating could not be created because the data didn't validate.
New edit following Iain Shelvington's recommendation the error is the following:
Ironically, this is the code that I did deem necessary to be in the post that is causing the problem. Which is weird considering this is a pure copy/paste of the pairing_rum. 'pairing_rum` even when empty does not return any error message, but the others do.
[23/Dec/2022 14:21:50] "GET /show_product/109 HTTP/1.1" 200 15430
<ul class="errorlist"><li>pairing_coffee<ul class="errorlist"><li>Select a valid choice. 0 is not one of the available choices.</li></ul></li><li>pairing_gin<ul class="errorlist"><li>Select a valid choice. 0 is not one of the available choices.</li></ul></li><li>pairing_whisky<ul class="errorlist"><li>Select a valid choice. 0 is not one of the available choices.</li></ul></li></ul>

Related

Django how to use a ModelChoiceField in a formset_factory

I am trying to use a modelchoicefield from the form in a formset_factory but i dont understand the error and don't know how to solve it.
(edit added the models.py)
models.py
class Order(Model):
user = ForeignKey(User, on_delete=SET_NULL, null=True)
fee = ForeignKey(Fee, on_delete=SET_NULL, null=True)
route = ForeignKey(Route, on_delete=SET_NULL, null=True)
price_rate = ForeignKey(PriceRate, on_delete=SET_NULL, null=True)
pallet_amount = IntegerField()
status = BooleanField()
postal_code = CharField(max_length=6)
city = CharField(max_length=255)
street = CharField(max_length=255)
delivery_from = DateTimeField()
delivery_until = DateTimeField(null=True)
created_at = DateTimeField(auto_now_add=True, blank=True)
updated_at = DateTimeField(auto_now=True)
deleted_at = DateTimeField(null=True, blank=True)
views.py
def routecreate_view(request):
orderformset = formset_factory(OrdersRouteForm, can_delete=False, extra=1)
if request.method == 'POST':
form = RouteForm(request.POST)
formset = orderformset(request.POST)
if form.is_valid() and formset.is_valid():
# process the data in form.cleaned_data as required
messages.success(request,
"You succesfully created an route.")
return HttpResponseRedirect(reverse('planner.dashboard'))
else:
form = RouteForm()
formset = orderformset()
return render(request, 'planner/route.html', {'form': form, 'formset': formset})
forms.py
class OrdersRouteForm(forms.ModelForm):
route = ModelChoiceField(
queryset=Order.objects.filter(status=1, delivery_until__gte=datetime.datetime.now(), deleted_at=None),
label='Order')
class Meta:
model = Order
fields = ("route",)
def __init__(self, *args, **kwargs):
super(OrdersRouteForm, self).__init__(*args, **kwargs)
for visible in self.visible_fields():
visible.field.widget.attrs['class'] = 'form-control m-2'
self.fields['route'].label_from_instance = self.label_from_instance
#staticmethod
def label_from_instance(obj):
return "pallets: %s, %s, %s, %s" % (obj.pallet_amount, obj.street, obj.city, obj.postal_code)
template:
{% extends 'base.html' %}
{% block base %}
<div class="container rounded bg-white mt-5 mb-5">
<div class="row">
<div class="col-md-5 border-right mx-auto">
planner//route
<div class="p-3 py-5">
<form id="form-container" method="POST">
{% csrf_token %}
{{ form }}
{{ formset }}
<button id="add-form" type="button">Add Another Bird</button>
<button class="btn btn-danger profile-button mt-3" onclick="window.history.back()">Cancel
</button>
<button class="btn btn-primary float-end mt-3" type="submit">Order</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
error:
Cannot assign "<Order: Order object (2)>": "Order.route" must be a "Route" instance.
The error occurs when i fill the formset with request.POST and then try to send the formset to the template.
I have tried an inlineformset and modelformset and didnt get it to work. ANY solution is welcome.
The problem seemed to be with the modelform. I removed this from the code:
class Meta:
model = Order
fields = ("route",)
I don't understand why this caused the problem and if anyone knows and can explain it please feel free to do so.
hope it helps anyone else with this problem.

Django UNIQUE constraint failed: players_comment.user_id

I'm trying to post comment but it's not getting posted, rather this error is appearing UNIQUE constraint failed: players_comment.user_id. I don't know why this error is occuring.
My forms.py:
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('body', 'transfernews')
My models.py :
class Transfernews(models.Model):
player_name = models.CharField(max_length=255)
player_image = models.CharField(max_length=2083)
player_description = models.CharField(max_length=3000)
date_posted = models.DateTimeField(default=timezone.now)
class Comment(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
transfernews = models.ForeignKey(Transfernews, related_name="comments", on_delete=models.CASCADE)
body = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '%s - %s' % (self.transfernews.player_name, self.user.username)
My views.py:
def transfer_targets(request):
transfernews = Transfernews.objects.all()
form = CommentForm(request.POST or None)
if form.is_valid():
new_comment = form.save(commit=False)
new_comment.user = request.user
new_comment.save()
return redirect('transfernews/')
return render(request, 'transfernews.html', {'transfernews': transfernews, 'form': form})
My transfernews.html:
{% for transfer in transfernews %}
{% if not transfer.comments.all %}
No comments Yet...
{% else %}
{% for comment in transfer.comments.all %}
<strong>
{{ comment.user.username }} - {{ comment.date_added }}
</strong>
<br/>
{{ comment.body }}
<br/><br/>
{% endfor %}
{% endif %}
<hr>
<div>Comment and let us know your thoughts</div>
<form method="POST">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-primary btn-sm shadow-none" type="submit">Post comment</button>
<button class="btn btn-outline-primary btn-sm ml-1 shadow-none" type="button">Cancel</button>
</form>
{% endfor %}
In models.py, in the comment class, change
user = models.OneToOneField(User, on_delete=models.CASCADE)
to
user = models.ForeignKey(to=User, on_delete=models.CASCADE)
One to one works both ways, user's allowed to only have one comment and a comment can belong to only one user. By changing to one to many via foreignkey you'll preserve the latter and get rid of the former constraint.

Django returning form error but the input seems to be valid

One of my models is a Product model which has 3 different price fields, so that one of its prices can be chosen when creating an order. I created a select input in one of my forms to allow the user to choose one of the prices, but when the form is submitted, Django returns an error telling me to introduce a valid number, even though I checked the request.POST and the value it's sending is a valid number.
models.py:
class Product(models.Model):
...
price_1 = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, default=0) # Separador de miles
price_2 = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, default=0) # Separador de miles
price_3 = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, default=0) # Separador de miles
forms.py:
class CartAddProductForm(forms.Form):
price = forms.DecimalField()
quantity = forms.IntegerField(
label ='Amount'
)
override = forms.BooleanField(required=False, initial=False, widget=forms.HiddenInput)
views.py:
#require_POST
def cart_add(request, product_id):
cart = Cart(request)
product = get_object_or_404(Product, id=product_id)
form = CartAddProductForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
cart.add(product=product, quantity=cd['quantity'], price=cd['price'], override_quantity=cd['override'])
return redirect('cart:cart_detail')
Template:
<form action="{% url 'cart:cart_add' product.id %}" method="post">
{% csrf_token %}
<div class="form-group">
<label for="id_price">Precio</label>
<select name="price" id="id_price" class="select2 form-control" data-toggle="select2" required>
<option value="{{ product.price_1 }}" selected>Precio retail: ${{ product.price_1|intcomma }}</option>
{% if product.price_2 %}
<option value="{{ product.price_2 }}">Precio mayorista: ${{ product.price_2|intcomma }}</option>
{% endif %}
{% if product.price_3 %}
<option value="{{ product.price_3 }}">Otro precio: ${{ product.price_3|intcomma }}</option>
{% endif %}
</select>
</div>
<div class="form-group">
<label for="id_quantity"></label>
<input data-toggle="touchspin" type="number" value="1" data-bts-max="100000" required id="id_quantity" name="quantity" data-bts-button-down-class="btn btn-danger" data-bts-button-up-class="btn btn-primary">
<input type="hidden" name="override" value="False" id="id_override">
</div>
<button type="submit" class="btn btn-secondary">
<i class="uil-plus-circle"></i>
Agregar a la orden
</button>
</form>
When debugging the view, form.is_valid() returns False. When checking form.errors, it effectively shows {'price': ['Enter a valid number.']}, but when checking request.POST, it shows 'price': ['40000,00'], 'quantity': ['1'], 'override': ['False'], so the number is valid, but for some reason it is not passing to the is_valid() method as a valid number. Why would that be?
Could this be a localization issue?
price = forms.DecimalField(decimal_places=2, localize=True)
The comma as decimal might be causing the form to fail.

Django form validation with FileField

I am having some trouble getting a file to post via a form using generic class-based view CreateView. Below is what i have so far. I am not quite sure how to handle the file and if request.FILES is getting the file being posted or if there is something else i need to be doing to capture the file information in the form. I have tried following the docs, however no luck in getting something working. File uploads as a blank field.
views.py
# Create
class FileUploadCreateView(BSModalCreateView):
template_name = 'fileupload/create-file.html'
form_class = FileUploadModelForm
success_message = 'Success: File was uploaded.'
success_url = reverse_lazy('files_list')
# Add required field my_user prior to posting form
def form_valid(self, form):
form = FileUploadModelForm(self.request.POST, self.request.FILES)
self.object = form.save(commit=False)
self.object.my_user = self.request.user
self.object.file_status = 'ready'
return super().form_valid(form)
forms.py
class FileUploadModelForm(BSModalModelForm):
class Meta:
model = FileUpload
fields = ['file_name', 'file_description', 'file_date_from', 'file_date_to','file_upload']
widgets = {
'file_name': forms.TextInput(attrs={'class':'form-control mb-3', 'id':'ruleset_name'}),
'file_description': forms.Textarea(attrs={'rows':5}),
'file_date_from': DatePickerInput(
options={
"format": "MM/DD/YYYY",
"showClose": True,
"showClear": True,
"showTodayButton": True,
}
),
'file_date_to': DatePickerInput(
options={
"format": "MM/DD/YYYY",
"showClose": True,
"showClear": True,
"showTodayButton": True,
}
),
'file_upload': forms.FileInput(attrs={'class':'form-control-file mb-3', 'id':'file_upload', 'type':'file', }),
}
html
{% load widget_tweaks %}
<form method="post" action="" enctype="multipart/form-data">
{% csrf_token %}
<div class="modal-header">
<h5 class="modal-title" style="color:#7a7a7a;">
<i class="fas fa-plus-square fa-med pr-2 align-middle"></i>
<span class="align-middle">ADD File</span>
</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="{% if form.non_field_errors %}invalid{% endif %} mb-2">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% for field in form %}
<div class="form-group">
<label for="{{ field.id_for_label }}" style="font-size: small; color:#7a7a7a;">{{ field.label }}</label>
{% render_field field class="form-control" %}
<div class="{% if field.errors %} invalid{% endif %}">
{% for error in field.errors %}
<p class="help-block">{{ error }}</p>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">Cancel</button>
<button type="button" class="submit-btn btn btn-primary pr-4 pl-4">Save</button>
</div>
</form>
model.py
class FileUpload(models.Model):
"""
Class for the creation of file uploads
"""
id = models.AutoField(primary_key=True)
my_user = models.ForeignKey('users.MyUser', on_delete=models.CASCADE, related_name='file_uploads', default=None)
file_name = models.CharField(max_length=255, blank=False, default=None, verbose_name='File Name')
file_description = models.CharField(max_length=255, blank=False, default=None, verbose_name='File Description')
file_date_from = models.DateField(default=None, null=True, blank=False, verbose_name='File Date From')
file_date_to = models.DateField(default=None, null=True, blank=False, verbose_name='File Date To')
STATUS_CHOICES = (
('ready','Ready'),
('processed', 'Processed'),
('archived','Archived'),
)
file_status = models.CharField(max_length=9, choices=STATUS_CHOICES, default=None, blank=False, verbose_name='File Status')
file_create_date = models.DateTimeField(verbose_name='File Create Date', auto_now_add=True)
file_upload = models.FileField(upload_to='uploads/%Y/%m/%d/', default=None, verbose_name='File Upload', blank=True)
class Meta:
ordering = ['-file_create_date']
constraints = [
models.UniqueConstraint(fields=['my_user','file_name'], name='Unique MyUser File')
]
def __str__(self):
return self.file_name
Pretty sure you forgot to add the enctype="multipart/form-data" data attribute to the form tag in your template.
To be sure, you would have to provide us with the Model, the Form and the template code.
after working on the view for some time, I was able to get the file to post via the following, using cleaned_data.
# Add required fields prior to posting form
def form_valid(self, form):
self.instance = form.save(commit=False)
self.instance.my_user = self.request.user
self.instance.file_status = 'ready'
self.instance.file_upload = form.cleaned_data['file_upload']
return super().form_valid(form)

Django form : Doesn't display any form errors

I'm getting an issue with my Django form validation. I would like to display form errors and make all fields required. I don't know why my fields can accept blank while blank is not defined in my model file.
This is my model :
class Customer(models.Model):
email = models.CharField(max_length=150, verbose_name=_('e-mail'), null=False)
first_name = models.CharField(max_length=70, verbose_name=_('first name'), null=False)
last_name = models.CharField(max_length=70, verbose_name=_('last name'), null=False)
country = models.ForeignKey(Country, verbose_name=_('country'))
institution = models.CharField(max_length=255, verbose_name=_('institution'), null=True)
creation_date = models.DateTimeField(auto_now_add=True, verbose_name=_('creation date'), null=False)
modification_date = models.DateTimeField(auto_now=True, verbose_name=_('modification date'), null=False)
class Meta:
verbose_name = _('customer')
verbose_name_plural = _('customer')
def __str__(self):
return f"{self.email}"
This is my form :
class CustomerForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(CustomerForm, self).__init__(*args, **kwargs)
self.fields['country'].empty_label = _('Select a country')
self.fields['country'].queryset = self.fields['country'].queryset.order_by(
'name')
for key in self.fields:
self.fields[key].required = True
class Meta:
model = Customer
fields = ['email', 'first_name', 'last_name', 'country', 'institution']
widgets = {
'email': forms.TextInput(attrs={'placeholder': _('name#example.com')}),
'first_name': forms.TextInput(attrs={'placeholder': _('First Name')}),
'last_name': forms.TextInput(attrs={'placeholder': _('Last Name')}),
'institution': forms.TextInput(attrs={'placeholder': _('Agency, company, academic or other affiliation')}),
}
You can find here my view with Django CBV :
class HomeView(CreateView):
""" Render the home page """
template_name = 'app/index.html'
form_class = CustomerForm
def get_context_data(self, **kwargs):
kwargs['document_list'] = Document.objects.all().order_by('publication__category__name')
return super(HomeView, self).get_context_data(**kwargs)
def post(self, request, *args, **kwargs):
if request.method != 'POST':
return HttpResponseRedirect(self.get_success_url())
form = self.form_class(request.POST)
email = request.POST['email']
country_id = request.POST['country']
country = Country.objects.get(id=country_id)
for checkbox in request.POST.getlist('DocumentChoice'):
document = Document.objects.get(id=checkbox)
token = self.gen_token(email, document.edqm_id)
Download.objects.create(email=email, country=country, pub_id=checkbox, token=token,
expiration_date=now + timedelta(minutes=10))
if not form.is_valid():
print('form invalid')
continue
return HttpResponseRedirect(self.get_success_url())
And finally my template :
{% extends "publication/base_backend.html" %}
{% load staticfiles %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block main %}
<form method="post" id="customerform" novalidate>
{% csrf_token %}
<h3>{% trans 'Your information' %}</h3>
<hr>
<div class="col-sm-12 col-md-12 col-lg-12">
{{ form.email|as_crispy_field:"bootstrap" }}
</div>
<br />
<br />
<br />
<br />
<div class="alert alert-info col-sm-12 col-md-12 col-lg-12" role="alert">
<small>{% trans "The fields below are optional if you have already requested a publication:" %}</small>
</div>
<div class="col-sm-5 col-md-5 col-lg-5">
{{ form.first_name|as_crispy_field:"bootstrap" }}<br>
{{ form.country|as_crispy_field:"bootstrap" }}
</div>
<div class="col-sm-5 col-md-5 col-lg-5 col-sm-offset-2 col-md-offset-2 col-lg-offset-2">
{{ form.last_name|as_crispy_field:"bootstrap" }}<br>
{{ form.institution|as_crispy_field:"bootstrap" }}
</div>
<div class="col-md-12">
<br />
<br />
</div>
<input type="submit" class="btn btn-default" value="{% trans 'Save' %}"/>
{% trans 'Cancel' %}
</form>
Issues :
According to required fields, I don't know why my form doesn't display missing values errors when I want to submit it.
I have to display fields as shown in my template because I have to make bootstrap design.
In order to display form errors, I have to write {{form.email.errors}} for example but nothing appears.
Thank you by advance