Django datalist not accepting any input as valid - django

I am trying to use a datalist input field to allow users to enter their own ingredients on Create a Recipe form. Ideally users can select a GlobalIngredient like salt, pepper, chicken, etc. already in the database or create their own. However, regardless of whether I enter a new ingredient or select a pre-existing ingredient, I am getting the following error: "Select a valid choice. That choice is not one of the available choices.". Unsure why I am getting this error?
Visual:
models.py
class Recipe(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
websiteURL = models.CharField(max_length=200, blank=True, null=True)
image = models.ImageField(upload_to='image/', blank=True, null=True)
name = models.CharField(max_length=220) # grilled chicken pasta
description = models.TextField(blank=True, null=True)
notes = models.TextField(blank=True, null=True)
serves = models.CharField(max_length=30, blank=True, null=True)
prepTime = models.CharField(max_length=50, blank=True, null=True)
cookTime = models.CharField(max_length=50, blank=True, null=True)
class Ingredient(models.Model):
name = models.CharField(max_length=220)
def __str__(self):
return self.name
class GlobalIngredient(Ingredient):
pass # pre-populated ingredients e.g. salt, sugar, flour, tomato
class UserCreatedIngredient(Ingredient): # ingredients user adds, e.g. Big Tomatoes
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
class RecipeIngredient(models.Model):
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
ingredient = models.ForeignKey(Ingredient, null=True, on_delete=models.SET_NULL)
description = models.TextField(blank=True, null=True)
quantity = models.CharField(max_length=50, blank=True, null=True) # 400
unit = models.CharField(max_length=50, blank=True, null=True) # pounds, lbs, oz ,grams, etc
forms.py
class RecipeIngredientForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(RecipeIngredientForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
#self.helper.form_id = 'id-entryform'
#self.helper.form_class = 'form-inline'
self.helper.layout = Layout(
Div(
Div(Field("ingredient", placeholder="Chickpeas - only write the ingredient here"), css_class='col-6 col-lg-4'),
Div(Field("quantity", placeholder="2 x 400"), css_class='col-6 col-md-4'),
Div(Field("unit", placeholder="grams"), css_class='col-5 col-md-4'),
Div(Field("description", placeholder="No added salt tins - All other information, chopped, diced, whisked!", rows='3'), css_class='col-12'),
css_class="row",
),
)
class Meta:
model = RecipeIngredient
fields = ['ingredient', 'quantity', 'unit', 'description']
labels = {
'ingredient': "Ingredient",
"quantity:": "Ingredient Quantity",
"unit": "Unit",
"description:": "Ingredient Description"}
widgets={'ingredient': forms.TextInput(attrs={
'class': 'dropdown',
'list' : 'master_ingredients',
'placeholder': "Chickpeas - only write the ingredient here"
})}
views.py
#login_required
def recipe_create_view(request):
ingredient_list = Ingredient.objects.all()
form = RecipeForm(request.POST or None)
# Formset = modelformset_factory(Model, form=ModelForm, extra=0)
RecipeIngredientFormset = formset_factory(RecipeIngredientForm)
formset = RecipeIngredientFormset(request.POST or None)
RecipeInstructionsFormset = formset_factory(RecipeInstructionForm, extra=0)
instructionFormset = RecipeInstructionsFormset(request.POST or None, initial=[{'stepName': "Step 1"}], prefix="instruction")
context = {
"form": form,
"formset": formset,
"instructionFormset": instructionFormset,
"ingredient_list": ingredient_list
}
if request.method == "POST":
print(request.POST)
if form.is_valid() and formset.is_valid() and instructionFormset.is_valid():
parent = form.save(commit=False)
parent.user = request.user
parent.save()
# formset.save()
#recipe ingredients
for form in formset:
child = form.save(commit=False)
print(child.ingredient)
globalIngredient = Ingredient.objects.filter(name=child.ingredient.lower()) # not truly global as this will return user ingredients too
if (globalIngredient):
pass
else:
newIngredient = UserCreatedIngredient(user=request.user, name=child.ingredient.lower())
newIngredient.save()
if form.instance.ingredient.strip() == '':
pass
else:
child.recipe = parent
child.save()
# recipe instructions
for instructionForm in instructionFormset:
instructionChild = instructionForm.save(commit=False)
if instructionForm.instance.instructions.strip() == '':
pass
else:
instructionChild.recipe = parent
instructionChild.save()
context['message'] = 'Data saved.'
return redirect(parent.get_absolute_url())
else:
form = RecipeForm(request.POST or None)
formset = RecipeIngredientFormset()
instructionFormset = RecipeInstructionsFormset()
return render(request, "recipes/create.html", context)
create.html
<!--RECIPE INGREDIENTS-->
{% if formset %}
<h3 class="mt-4 mb-3">Ingredients</h3>
{{ formset.management_form|crispy }}
<div id='ingredient-form-list'>
{% for ingredient in formset %}
<div class='ingredient-form'>
{% crispy ingredient %}
</div>
{% endfor %}
<datalist id="master_ingredients">
{% for k in ingredient_list %}
<option value="{{k.name|title}}"></option>
{% endfor %}
</datalist>
</div>
<div id='empty-form' class='hidden'>
<div class="row mt-4">
<div class="col-6">{{ formset.empty_form.ingredient|as_crispy_field }}</div>
<div class="col-6">{{ formset.empty_form.quantity|as_crispy_field }}</div>
<div class="col-6">{{ formset.empty_form.unit|as_crispy_field }}</div>
<div id="ingredientIdForChanging" style="display: none;"><div class="col-12">{{ formset.empty_form.description|as_crispy_field }}</div><button type="button"
class="btn btn-outline-danger my-2" onclick="myFunction('showDescription')"><i class="bi bi-dash-circle"></i> Hide
Description</button></div><button type="button"
class="btn btn-outline-primary col-5 col-md-3 col-lg-3 col-xl-3 m-2" id="ingredientIdForChanging1"
onclick="myFunction('showDescription')"><i class="bi bi-plus-circle"></i> Add a
Description Field</button>
</div>
</div>
<button class="btn btn-success my-2" id='add-more' type='button'>Add more ingredients</button>
{% endif %}

Related

How to save Django Form and Formset at the same time

I have a page in which I use a form and formset at the same time. The form is for the thesis information and the formset is for the author.
This is the code in my models.py
class thesisDB(Model):
thesis_id = models.AutoField(primary_key=True, blank=True, null=False)
title = models.CharField(max_length=200, blank=True, null=True, unique=True)
published_date = models.DateField(blank=True, null=True)
pdf = models.FileField(upload_to='pdf/', blank=True, null=True ,validators=[FileExtensionValidator(['pdf'])],)
course = models.ForeignKey(ColCourse, default=None, on_delete=models.CASCADE, verbose_name='Course')
tags = TaggableManager()
date_created = models.DateField(auto_now_add=True, blank=True, null=True )
uploaded_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, default=None)
published_status = models.CharField(max_length=10, choices= THESIS_DECISION, default='Pending')
abstract = models.TextField(blank=True, null=True)
hit_count_generic = GenericRelation(HitCount, object_id_field='object_pk', related_query_name='hit_count_generic_relation')
reason = models.TextField(blank=True, null=True)
slug = models.SlugField(max_length = 250, null = True, blank = True)
def save(self, *args, **kwargs):
self.title = self.title.title()
#self.author = self.author.title()
self.slug = slugify(self.title + "-" + self.author + "-" + str(self.published_date))
super().save(*args, **kwargs)
class Meta:
db_table = 'thesis_storage'
initial_validator = RegexValidator('[A-Z]+.', 'Initial must contain period')
class Authors(Model):
thesis = models.ForeignKey(thesisDB, on_delete=models.CASCADE)
first_name = models.CharField(max_length=200, blank=True, null=True)
last_name = models.CharField(max_length=200, blank=True, null=True)
middle_initial = models.CharField(max_length=2, blank=True, null=True, validators=[initial_validator])
This is the code in my forms.py:
class AuthorForm(forms.ModelForm):
class Meta:
model = Authors
exclude = ()
widgets = {
'first_name': forms.TextInput(attrs=
{'placeholder': 'First Name', 'class':'form-control', 'required': 'required'}),
'last_name': forms.TextInput(attrs=
{'placeholder': 'Last Name', 'class':'form-control', 'required': 'required'}),
'middle_initial': forms.TextInput(attrs=
{'placeholder': 'M.I.', 'class':'form-control', 'required': 'required'}),
}
AuthorFormSet = inlineformset_factory(thesisDB, Authors, form=AuthorForm,
fields=['first_name', 'last_name', 'middle_initial'], extra=3, can_delete=False)
class thesisForm(forms.ModelForm):
class Meta:
model = thesisDB
fields = ('title', 'published_date', 'course','tags', 'abstract', 'pdf',)
readonly_fields = ['date_created']
course = forms.ModelChoiceField(queryset=ColCourse.objects.all().order_by('-course_name'))
# abstract = forms.CharField(widget=CKEditorWidget())
# problem = forms.CharField(widget=CKEditorWidget())
widgets = {
'title': forms.TextInput(attrs=
{'placeholder': 'Title', 'class':'form-control', 'required': 'required'}),
'published_date': forms.DateInput(attrs=
{'class':'form-control', 'required': 'required', 'type':'date'}),
'abstract': forms.Textarea(attrs=
{'placeholder': 'Abstract', 'class':'form-control',}),
'course': forms.Select(attrs=
{'placeholder': 'Short Description', 'class':'regDropDown', 'required': 'required'}),
}
def __init__(self, *args, **kwargs):
super(thesisForm, self).__init__(*args, **kwargs)
# Making location required
self.fields['pdf'].required = True
self.fields['abstract'].required = True
self.fields['tags'].required = True
And this is my views.py for submitting the form.
#login_required
#for_students
def submission(request):
formset = AuthorFormSet()
if request.method == "POST":
form = thesisForm(request.POST, request.FILES)
if form.is_valid() and formset.is_valid():
form.instance.uploaded_by = request.user # set the user
post = form.save() # save the form
formset.instance.thesis = form.instance.thesis_id
post = formset.save()
message = "Your project entitled %s has been submitted successfully! We will notify you through email if it is once evaluated" % form.instance.title
messages.success(request, message)
return redirect('/profile/personal_repository/')
else:
form = thesisForm()
formset = AuthorFormSet()
return render(request,'StudentSubmitProject.html',{'form':form, 'formset':formset})
And my template file:
<div class="container-fluid profilecontainer">
<div class="row">
<div class="col-lg-2"></div>
<div class="col-lg-8 right_section">
<h4 class="textmaroon">Submit A Project</h4>
<p>The requirements for submitting a thesis follows:<p>
<ul>
<li>The softcopy of your thesis which is in PDF format.</li>
<li>The pdf file should contain the approval form attesting the thesis has been approved by your department.</li>
<li>The descriptions of thesis such as its Title, Author, Publication Date, Course, and Keywords.</li>
<li>The abstract text of your thesis which should match in the pdf file.</li>
</ul>
<p>Below is the form for submitting a thesis project. Your project would be evaluated first by the Admin Account before it can be available to access in the system, so ensure that the given details are valid to avoid possible rejection.</p>
<hr></hr>
<form method="POST" class="mt-5 form-group profile_form" enctype="multipart/form-data">
{{formset.management_data }}
{% csrf_token %}
<h4 class="textmaroon">Add author</h4>
<p>Enter the author/s of the thesis project</p>
<hr></hr>
<div class="row">
{% for form_s in formset %}
<div class="col-md-5">
{{form_s.first_name | as_crispy_field}}
</div>
<div class="col-md-5">
{{form_s.last_name | as_crispy_field}}
</div>
<div class="col-md-2">
{{form_s.middle_initial | as_crispy_field}}
</div>
{% endfor %}
</div>
<h4 class="textmaroon mt-5">Describe the thesis</h4>
<p>Define the thesis project</p>
<hr></hr>
<div class="row">
<div class="col-md-12">
{{form | crispy}}
</div>
</div>
<div class="row">
<div class="col-md-12">
<button type="submit" class="btn btn-danger mt-3 float-end">Submit</button>
<button type="reset" class="btn btn-secondary mt-3 me-2 float-end">Reset</button>
</div>
</div>
</form>
</div>
<div class="col-lg-2"></div>
</div>
</div>
My problem is that the entered data in the form save, but not in the formset as well.
because you didn't add request.POST to your formset, so no data was passed in the formset:
if request.method == "POST":
formset = AuthorFormSet(request.POST) #need request.POST
form = thesisForm(request.POST, request.FILES)
if form.is_valid() and formset.is_valid():
if there is also file in the formset, add request.FILES as second arg

Datalist with free text error "Select a valid choice. That choice is not one of the available choices."

I am building a Create a Recipe form using crispy forms and I am trying to use a datalist input field for users to enter their own ingredients, like 'Big Tomato' or select from GlobalIngredients already in the database like 'tomato' or 'chicken'. However, regardless of whether I enter a new ingredient or select a pre-existing one, I am getting the following error: "Select a valid choice. That choice is not one of the available choices.". How do I fix this error?
Visual:
models.py
class Recipe(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
websiteURL = models.CharField(max_length=200, blank=True, null=True)
image = models.ImageField(upload_to='image/', blank=True, null=True)
name = models.CharField(max_length=220) # grilled chicken pasta
description = models.TextField(blank=True, null=True)
notes = models.TextField(blank=True, null=True)
serves = models.CharField(max_length=30, blank=True, null=True)
prepTime = models.CharField(max_length=50, blank=True, null=True)
cookTime = models.CharField(max_length=50, blank=True, null=True)
class Ingredient(models.Model):
name = models.CharField(max_length=220)
def __str__(self):
return self.name
class GlobalIngredient(Ingredient):
pass # pre-populated ingredients e.g. salt, sugar, flour, tomato
class UserCreatedIngredient(Ingredient): # ingredients user adds, e.g. Big Tomatoes
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
class RecipeIngredient(models.Model):
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
ingredient = models.ForeignKey(Ingredient, null=True, on_delete=models.SET_NULL)
description = models.TextField(blank=True, null=True)
quantity = models.CharField(max_length=50, blank=True, null=True) # 400
unit = models.CharField(max_length=50, blank=True, null=True) # pounds, lbs, oz ,grams, etc
forms.py
class RecipeIngredientForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(RecipeIngredientForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
#self.helper.form_id = 'id-entryform'
#self.helper.form_class = 'form-inline'
self.helper.layout = Layout(
Div(
Div(Field("ingredient", placeholder="Chickpeas - only write the ingredient here"), css_class='col-6 col-lg-4'),
Div(Field("quantity", placeholder="2 x 400"), css_class='col-6 col-md-4'),
Div(Field("unit", placeholder="grams"), css_class='col-5 col-md-4'),
Div(Field("description", placeholder="No added salt tins - All other information, chopped, diced, whisked!", rows='3'), css_class='col-12'),
css_class="row",
),
)
class Meta:
model = RecipeIngredient
fields = ['ingredient', 'quantity', 'unit', 'description']
labels = {
'ingredient': "Ingredient",
"quantity:": "Ingredient Quantity",
"unit": "Unit",
"description:": "Ingredient Description"}
widgets={'ingredient': forms.TextInput(attrs={
'class': 'dropdown',
'list' : 'master_ingredients',
'placeholder': "Chickpeas - only write the ingredient here"
})}
views.py
#login_required
def recipe_create_view(request):
ingredient_list = Ingredient.objects.all()
form = RecipeForm(request.POST or None)
# Formset = modelformset_factory(Model, form=ModelForm, extra=0)
RecipeIngredientFormset = formset_factory(RecipeIngredientForm)
formset = RecipeIngredientFormset(request.POST or None)
RecipeInstructionsFormset = formset_factory(RecipeInstructionForm, extra=0)
instructionFormset = RecipeInstructionsFormset(request.POST or None, initial=[{'stepName': "Step 1"}], prefix="instruction")
context = {
"form": form,
"formset": formset,
"instructionFormset": instructionFormset,
"ingredient_list": ingredient_list
}
if request.method == "POST":
print(request.POST)
if form.is_valid() and formset.is_valid() and instructionFormset.is_valid():
parent = form.save(commit=False)
parent.user = request.user
parent.save()
# formset.save()
#recipe ingredients
for form in formset:
child = form.save(commit=False)
print(child.ingredient)
globalIngredient = Ingredient.objects.filter(name=child.ingredient.lower()) # not truly global as this will return user ingredients too
if (globalIngredient):
pass
else:
newIngredient = UserCreatedIngredient(user=request.user, name=child.ingredient.lower())
newIngredient.save()
if form.instance.ingredient.strip() == '':
pass
else:
child.recipe = parent
child.save()
# recipe instructions
for instructionForm in instructionFormset:
instructionChild = instructionForm.save(commit=False)
if instructionForm.instance.instructions.strip() == '':
pass
else:
instructionChild.recipe = parent
instructionChild.save()
context['message'] = 'Data saved.'
return redirect(parent.get_absolute_url())
else:
form = RecipeForm(request.POST or None)
formset = RecipeIngredientFormset()
instructionFormset = RecipeInstructionsFormset()
return render(request, "recipes/create.html", context)
create.html
<!--RECIPE INGREDIENTS-->
{% if formset %}
<h3 class="mt-4 mb-3">Ingredients</h3>
{{ formset.management_form|crispy }}
<div id='ingredient-form-list'>
{% for ingredient in formset %}
<div class='ingredient-form'>
{% crispy ingredient %}
</div>
{% endfor %}
<datalist id="master_ingredients">
{% for k in ingredient_list %}
<option value="{{k.name|title}}"></option>
{% endfor %}
</datalist>
</div>
<div id='empty-form' class='hidden'>
<div class="row mt-4">
<div class="col-6">{{ formset.empty_form.ingredient|as_crispy_field }}</div>
<div class="col-6">{{ formset.empty_form.quantity|as_crispy_field }}</div>
<div class="col-6">{{ formset.empty_form.unit|as_crispy_field }}</div>
<div id="ingredientIdForChanging" style="display: none;"><div class="col-12">{{ formset.empty_form.description|as_crispy_field }}</div><button type="button"
class="btn btn-outline-danger my-2" onclick="myFunction('showDescription')"><i class="bi bi-dash-circle"></i> Hide
Description</button></div><button type="button"
class="btn btn-outline-primary col-5 col-md-3 col-lg-3 col-xl-3 m-2" id="ingredientIdForChanging1"
onclick="myFunction('showDescription')"><i class="bi bi-plus-circle"></i> Add a
Description Field</button>
</div>
</div>
<button class="btn btn-success my-2" id='add-more' type='button'>Add more ingredients</button>
{% endif %}
You can create your own TextInput and TypedModelListField field to handle this. I think what you're looking for is something which allows the user to both search and provide a recommended selection of choices but validate the input against a model (Ingredient).
I've created one here:
class TypedModelListField(forms.ModelChoiceField):
def to_python(self, value):
if self.required:
if value == '' or value == None:
raise forms.ValidationError('Cannot be empty')
validate_dict = {self.validate_field: value}
try:
value = type(self.queryset[0]).objects.get(**validate_dict))
except:
raise forms.ValidationError('Select a valid choice. That choice is not one of the available choices.')
value = super().to_python(value)
return value
def __init__(self, *args, **kwargs):
self.validate_field= kwargs.pop('validate_field', None)
super().__init__(*args, **kwargs)
class ListTextWidget(forms.TextInput):
def __init__(self, dataset, name, *args, **kwargs):
super().__init__(*args)
self._name = name
self._list = dataset
self.attrs.update({'list':'list__%s' % self._name,'style': 'width:100px;'})
if 'width' in kwargs:
width = kwargs['width']
self.attrs.update({'style': 'width:{}px;'.format(width)})
if 'identifier' in kwargs:
self.attrs.update({'id':kwargs['identifier']})
def render(self, name, value, attrs=None, renderer=None):
text_html = super().render(name, value, attrs=attrs)
data_list = '<datalist id="list__%s">' % self._name
for item in self._list:
data_list += '<option value="%s">' % item
data_list += '</datalist>'
return (text_html + data_list)
Within the RecipeIngredientForm add the following definition:
ingredient = TypedModelListField(
queryset=Ingredient.objects.all(),
validate_field='name')
And then in RecipeIngredientForm within the __init__ function. Include the following after the super() is called.
self.fields['ingredient'].widget = ListTextWidget(
dataset=Ingredient.objects.all(),
name='ingredient_list')
With ecogels comment I was able to understand what was causing the issue and with a combination of Lewis answer and this answer I managed to get this working with the following code.
fields.py
from django import forms
class ListTextWidget(forms.TextInput):
def __init__(self, data_list, name, *args, **kwargs):
super(ListTextWidget, self).__init__(*args, **kwargs)
self._name = name
self._list = data_list
self.attrs.update({'list':'list__%s' % self._name})
def render(self, name, value, attrs=None, renderer=None):
text_html = super(ListTextWidget, self).render(name, value, attrs=attrs)
data_list = '<datalist id="list__%s">' % self._name
for item in self._list:
data_list += '<option value="%s">' % str(item).title()
data_list += '</datalist>'
return (text_html + data_list)
forms.py
from .fields import ListTextWidget
class RecipeIngredientForm(forms.ModelForm):
ingredientName = forms.CharField(required=True)
def __init__(self, *args, **kwargs):
super(RecipeIngredientForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Div(
Div(Field("ingredientName", placeholder="Chickpeas - only write the ingredient here"), css_class='col-6 col-lg-4'),
Div(Field("quantity", placeholder="2 x 400"), css_class='col-6 col-md-4'),
Div(Field("unit", placeholder="grams"), css_class='col-5 col-md-4'),
Div(Field("description", placeholder="No added salt tins - All other information, chopped, diced, whisked!", rows='3'), css_class='col-12'),
css_class="row",
),
)
self.fields['ingredientName'].widget = ListTextWidget(data_list=Ingredient.objects.all(), name='ingredient-list')
class Meta:
model = RecipeIngredient
fields = ['ingredientName', 'quantity', 'unit', 'description']
labels = {
'ingredientName': "Ingredient",
"quantity:": "Ingredient Quantity",
"unit": "Unit",
"description:": "Ingredient Description"}
create.html:
<!--RECIPE INGREDIENTS-->
{% if formset %}
<h3 class="mt-4 mb-3">Ingredients</h3>
{{ formset.management_form|crispy }}
<div id='ingredient-form-list'>
{% for ingredient in formset %}
<div class='ingredient-form'>
{% crispy ingredient %}
</div>
{% endfor %}
</div>
<div id='empty-form' class='hidden'>
<div class="row mt-4">
<div class="col-6">{{ formset.empty_form.ingredientName|as_crispy_field }}</div>
<div class="col-6">{{ formset.empty_form.quantity|as_crispy_field }}</div>
<div class="col-6">{{ formset.empty_form.unit|as_crispy_field }}</div>
<div id="ingredientIdForChanging" style="display: none;"><div class="col-12">{{ formset.empty_form.description|as_crispy_field }}</div><button type="button"
class="btn btn-outline-danger my-2" onclick="myFunction('showDescription')"><i class="bi bi-dash-circle"></i> Hide
Description</button></div><button type="button"
class="btn btn-outline-primary col-5 col-md-3 col-lg-3 col-xl-3 m-2" id="ingredientIdForChanging1"
onclick="myFunction('showDescription')"><i class="bi bi-plus-circle"></i> Add a
Description Field</button>
</div>
</div>
<button class="btn btn-success my-2" id='add-more' type='button'>Add more ingredients</button>
{% endif %}
views.py changes:
form = RecipeForm(request.POST or None)
# Formset = modelformset_factory(Model, form=ModelForm, extra=0)
RecipeIngredientFormset = formset_factory(RecipeIngredientForm)
formset = RecipeIngredientFormset(request.POST or None)
RecipeInstructionsFormset = formset_factory(RecipeInstructionForm, extra=0)
instructionFormset = RecipeInstructionsFormset(request.POST or None, initial=[{'stepName': "Step 1"}], prefix="instruction")
URLForm = RecipeIngredientURLForm(request.POST or None)
context = {
"form": form,
"formset": formset,
"URLForm": URLForm,
"instructionFormset": instructionFormset
}

Populate dropdown in template from model choices using Django Rest Framework

I have the model, serializer, viewset and html as follows:
GENDER = (
('MALE', 'Male'),
('FEMALE', 'Female'),
('OTHERS', 'Others'),
)
class Client(BaseModel):
first_name = models.CharField(max_length=256)
last_name = models.CharField(max_length=256, default="", blank=True, null=True)
designation = models.CharField(max_length=256, default="", blank=True, null=True)
gender = models.CharField(max_length=20, choices=GENDER, null=True, blank=True)
class ClientSerializer(QueryFieldsMixin, DynamicFieldsModelSerializer):
name = serializers.SerializerMethodField()
def get_name(self, obj):
return getattr(obj, "first_name", "") + " " + getattr(obj, "last_name", "")
class Meta:
model = Client
fields = '__all__'
#method_decorator(login_required, name='dispatch')
class ClientViewSet(viewsets.ModelViewSet):
model = Client
queryset = model.objects.all()
serializer_class = ClientSerializer
#action(detail=True, methods=['post','get'], renderer_classes=[renderers.TemplateHTMLRenderer])
def update_client(self, request, *args, **kwargs):
object = self.get_object()
context = {"operation": "Update",
"object_id": object.id,
"events": Event.GetEventsForObject(object)}
template_name = 'contact-client.html'
response = Response(context, template_name=template_name)
<form id="client-form" method="" action="" enctype="multipart/form-data" operation="{{operation}}">
{% csrf_token %}
<div class="row">
<div class="columns medium-5 medium-text-left">
<div class="select2-full select-2-full--sm input-rounded">
<label for = "gender" style="text-align: left;" >Gender</label>
<select id="gender" class="js-select2 input-height-sm element-contact" name="gender" validated="false"></select>
<option></option>
</div>
<div id="gender_error" style="display:none">
<p class="help-text"> <span class="form-action-icon error-icon"></span>Please select gender.</p>
</div>
</div>
<div class="columns medium-5 medium-text-left">
</div>
</div>
</form>
When I instantiate the ClientSerializer in shell like this ClientSerializer() then that gender field is shown along with its choices. But I am not able to show it in the template. All the other fields are being passed correctly.
How can I populate the dropdown with the values of choices in template? What should I pass in the option tag to display the choices and send the data to the view?
Use a ChoiceField instead of CharField
Replace this line
gender = models.CharField(max_length=20, choices=GENDER, null=True, blank=True)
with this one
gender = models.ChoiceField(max_length=20, choices=GENDER, null=True, blank=True)
To show all the choices in your template you can do something like this
from my_app.models import gender
context = {"choices" : gender}
response = Response(context,template_name)
And in template
{% for choice in choices %}
<option value="{{ choice }}">{{ choice }}</option>
{% endfor %}
If you are using the django-template then you can create a model form and pass that form as render context and then render that inside a form.
More on model form: https://docs.djangoproject.com/en/3.1/topics/forms/modelforms/

In Django getting errors based on dropdown selection

Getting error when fields are selected from the down. Not seeing why it is throwing error
Django Dropdown form.error: ERROR ALERT:
location_name Select a valid choice. That choice is not one of the available choices.
Here is the model, form, view and html looks like
MODEL
class Code (models.Model):
name = models.CharField(max_length=4, default=None, blank=True)
def __str__(self): return self.name
class Device (models.Model):
code = models.ForeignKey(Code, on_delete=models.CASCADE, null=True)
ip = models.GenericIPAddressField(protocol='IPv4', unique=True)
def __str__(self): return self.ip
class SiteData (models.Model):
site = models.ForeignKey(Code, on_delete=models.SET_NULL, null=True)
site_ip = models.ForeignKey(Device, on_delete=models.SET_NULL, null=True)
site_data1 = models.CharField(max_length=3, default='120')
class CombineData(models.Model):
location = models.ForeignKey(Code, on_delete=models.SET_NULL, null=True)
device = models.ForeignKey(AddData, on_delete=models.SET_NULL, null=True)
locdata = models.ForeignKey(SiteData, on_delete=models.SET_NULL, null=True)
FORM
class CombineData_form(forms.ModelForm):
class Meta:
model = P2_NexusLeafPair
fields = '__all__'
VIEW
def comboView(request, *args, **kwargs):
template_name = 'site_display.html'
code = Code.objects.order_by('-name')
device = Device.objects.order_by('-ip')
sitename = SiteData.objects.order_by('-site')
content = {
'code': code,
'device': device,
'sitename': sitename
}
if request.method == 'POST':
form = CombineData_form(request.POST or None)
print(form.is_valid())
#print(form.errors)
if form.is_valid():
. . .
else:
messages.error(request, form.errors)
else:
form = CombineData_form()
return render(request, template_name, content)
HTML
<form id="comboView" class="post-form" role=form method="POST" action="comboView">{% csrf_token %}
<div name="code" class="dropdown" id="mainselection" required>
<select name="dc_name">
<option class="dropdown-item" value="">---------</option>
{% for item in code %}
<option value="{{ item }}">{{ item }}</option>
{% endfor %}
</div>
<input type="submit" value="Submit" />
</form>
{same as other fields: device, sitename}
{% for item in content %}
{{item.code}}
{% endfor %}
Try that.

I can not see the saved image in the database name 'user' is not defined

I'm uploading images, and they appear inside the Static / Media folder
but I can not show these images inside the perfil.html
and using the shell of the error:
Erro:
name 'user' is not defined
but I'm logged in because the other options that user needs logged in as they are working like:
views.py
def gallery(request):
gallery = Gallery.objects.all()
form = GalleryForm()
data = {'gallery': gallery, 'form': form}
return render(request, 'gallery.html', data)
def gallery_novo(request):
if request.method == 'POST':
form = GalleryForm(request.POST, request.FILES)
if form.is_valid():
my_novo_gallery = form.save(commit=False) #save no commit
my_novo_gallery.user=request.user #set user
my_novo_gallery.save() #save to db
return redirect('sistema_perfil')
else:
form = GalleryForm
return render(request, 'gallery.html', {'form': form})
def perfil(request):
usuario = Usuario.objects.all()
form = UsuarioForm()
data = {'usuario': usuario, 'form': form}
gallery = Gallery.objects.all()
form2 = GalleryForm()
data2 = {'gallery': gallery, 'form2': form2}
return render(request, 'perfil.html', data, data2)
models.py
class Usuario(models.Model):
nome = models.CharField(max_length=50, blank=False)
sobrenome = models.CharField(max_length=50, blank=False)
user = models.OneToOneField(User, on_delete=models.CASCADE)
email_confirmed = models.BooleanField(default=False)
email = models.EmailField(blank=False)
foto = StdImageField( blank=False, variations={
'large': (600, 400),
'thumbnail': (100, 100, True),
'medium': (300, 200),
})
telefone = models.CharField(max_length=20, blank=False, verbose_name="Celular")
cpf = models.CharField(max_length=19)
data_nascimento = models.CharField(max_length=8, blank=False, verbose_name="Data de nascimento")
sexo = models.CharField(default='M', max_length=2, choices=SEXO_CHOICES)
pet = MultiSelectField( max_length=100, choices=PET_CHOICES, verbose_name="Selecione seus pets")
endereco = models.CharField(max_length=50)
numero = models.CharField(max_length=10)
bairro = models.CharField(max_length=30)
cep = models.CharField(max_length=25)
cidade = models.CharField(max_length=30)
estado = models.CharField(default='RS', max_length=3, choices=STATE_CHOICES)
password1 = models.CharField(max_length=15, blank=False)
about = models.TextField(max_length=1000, blank=False, verbose_name="Sobre vocĂȘ")
def __unicode__(self):
return self.nome
#receiver(post_save, sender=User)
def cadastro_novo(sender, instance, created, **kwargs):
if created:
Usuario.objects.create(user=instance)
instance.usuario.save()
def __str__(self):
return str(self.nome) + ' - ' + str(self.email) + ' - ' + str(self.telefone)
class Gallery(models.Model):
gallery = StdImageField( blank=False, variations={
'large': (600, 400),
'thumbnail': (100, 100, True),
'medium': (300, 200),
})
titulo = models.CharField(max_length=50, blank=False)
usuario_id = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
forms.py
class GalleryForm(forms.ModelForm):
gallery = forms.FileField(
widget=forms.ClearableFileInput(attrs={'multiple': 'True'}))
titulo = forms.CharField()
class Meta:
model = Gallery
fields = ( 'gallery', 'titulo')
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
return super(GalleryForm, self).__init__(*args, **kwargs)
def save(self, commit=True, user=None):
form = super(GalleryForm, self).save(commit=False)
form.usario_id = user
if commit:
form.save()
return form
gallery.html
{%extends 'bases/base.html' %}
{% load static %}
{% load bootstrap %}
{% load widget_tweaks %}
{% load crispy_forms_tags %}
{% block main %}
<form class="exampleone" action="{% url 'sistema_gallery_novo' %}" method="POST" enctype="multipart/form-data" id="form" name="form" validate >
{% csrf_token %}
<div class="form-row">
<div class="form-group col-md-4">
{{ form.gallery | as_crispy_field }}
</div>
<div class="form-group col-md-4">
{{ form.titulo | as_crispy_field }}
</div>
<div class="form-group col-md-4">
</div>
</div>
</div>
<button type="submit" class="btn btn-primary btn-block">Cadastrar</button>
</form>
</div> </div>
<div>
{% endblock %}
urls.py
url(r'gallery/$', gallery, name='sistema_gallery'),
url(r'gallery-novo/$', gallery_novo, name='sistema_gallery_novo'),
url(r'perfil/$', perfil, name='sistema_perfil'),
perfil.html
{% for g in user.usuario.gallery_set.all %}
<img src="{{ g.medium.url}}">
{% endfor %}