I posted a screenshot below to hopefully make this easier to understand so I will reference it in my question.
I am trying to create a recipe cost calculator app. I have 3 model namely Recipe, RecipeIngredient and Ingredient
The user will add ingredients to the database and then create recipes using those ingredients throught the RecipeIngredient model.
When creating an Ingredient the user selects a unit eg. (grams). Now when the user goes to create a Recipe (see screenshot below) I want to only display the units that are relevant to that ingredient and not all of them for eg. A user added Beef and the unit was grams now when the user is adding a RecipeIngredient to the Recipe I want them to only be able to select from the "Weight" category in the example (see screenshot below). The reason for this is because grams can't be converted to milliliters and so it shouldn't even be a choice.
If anything is unclear or you need more information just let me know.
Models
Recipe Models
from django.db import models
from django.urls import reverse
from django.contrib.auth import get_user_model
from ingredients.models import Ingredient
class Recipe(models.Model):
"""Store recipe information and costs"""
class Meta:
"""Meta definition for Recipe."""
verbose_name = "Recipe"
verbose_name_plural = "Recipes"
ordering = ("name",)
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
name = models.CharField(max_length=155)
yield_count = models.PositiveIntegerField()
yield_units = models.CharField(max_length=155, default="servings")
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse("recipe:detail", kwargs={"id": self.pk})
def get_update_url(self):
return reverse("recipe:update", kwargs={"id": self.pk})
class RecipeIngredient(models.Model):
"""Holds more information about how much of a certain ingredient is used inside a recipe"""
class Meta:
"""Meta definition for RecipeIngredient"""
verbose_name = "RecipeIngredient"
verbose_name_plural = "RecipeIngredients"
# NOTE: User should first choose the ingredient
recipe = models.ForeignKey(Recipe, related_name="ingredients", on_delete=models.CASCADE)
ingredient = models.ForeignKey(Ingredient, related_name="ingredient", on_delete=models.CASCADE)
# NOTE: Then the amount an unit should be asked based on that
amount = models.DecimalField(max_digits=20, decimal_places=2)
unit = models.CharField(max_length=10, choices=Ingredient.UNIT_CHOICES, default=None)
def __str__(self):
"""Unicode representation of RecipeIngredient"""
return self.ingredient.__str__()
Ingredient Model
from django.db import models
from django.urls import reverse
from django.contrib.auth import get_user_model
from pint import UnitRegistry
from .choices import ALL_UNIT_CHOICES
class Ingredient(models.Model):
"""Stores data about an ingredient to be used by a Recipe model."""
class Meta:
"""Meta definition for Ingredient."""
verbose_name = "Ingredient"
verbose_name_plural = "Ingredients"
ordering = ("name",)
# Unit choices that will display in a dropdown
# It will be ordered by group (eg. Weight, Volume)
UNIT_CHOICES = ALL_UNIT_CHOICES
ureg = UnitRegistry()
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
name = models.CharField(max_length=50, unique=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
size = models.DecimalField(max_digits=10, decimal_places=2)
unit = models.CharField(max_length=10, choices=UNIT_CHOICES, default=None)
def __str__(self):
"""Unicode representation of Ingredient."""
return self.name
def get_absolute_url(self):
"""Return absolute url for Ingredient"""
return reverse("ingredient:detail", kwargs={"id": self.pk})
def get_update_url(self):
"""Return the url for update page"""
return reverse("ingredient:update", kwargs={"id": self.pk})
def get_delete_url(self):
return reverse("ingredient:delete", kwargs={"id": self.pk})
def save(self, *args, **kwargs):
self.unit = self.ureg.Unit(self.unit)
super(Ingredient, self).save(*args, **kwargs)
Views
def create_recipe(request):
template_name = "recipes/create.html"
form = CreateRecipeForm(request.POST or None)
ingredient_choice_form = IngredientChoiceForm(request.POST or None, request=request)
Formset = modelformset_factory(RecipeIngredient, form=CreateRecipeIngredientForm, extra=0)
formset = Formset(request.POST or None, queryset=Recipe.objects.none())
context = {
"form": form,
"ingredient_choice_form": ingredient_choice_form,
"formset": formset,
}
print(request.POST)
return render(request, template_name, context)
# This is the view being calle by HTMX everytime an ingredient is selected
def get_recipe_ingredient_details(request):
template_name = "recipes/hx_snippets/add_recipe_ingredient_details.html"
ingredient_id = request.GET.get("ingredients")
if not ingredient_id:
return HttpResponse("")
ingredient = Ingredient.objects.get(id=ingredient_id)
choices = get_unit_group(ingredient.unit)
Formset = modelformset_factory(RecipeIngredient, form=CreateRecipeIngredientForm, extra=1)
formset = Formset(
request.POST or None,
queryset=RecipeIngredient.objects.none(),
initial=[
{
"ingredient": ingredient,
"unit": choices,
},
],
)
context = {
"formset": formset,
"ingredient": ingredient,
}
Templates
Template returned by HTMX request
<p>
{% for form in formset %}
{{ form }}
{% endfor %}
</p>
main create template
{% block content %}
<h1>Create Recipe</h1>
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<h3>Add ingredients</h3>
{{ formset.management_form }}
<div id="ingredient-form"></div>
{{ ingredient_choice_form.as_p }} <-- this is an HTMX get request that calls the get_recipe_ingredient_details view
<p><button type="submit">Create</button></p>
</form>
{% endblock content %}
{% block scripts %}
<script src="{% static 'js/recipes/addFormsetField.js' %}"></script>
{% endblock scripts %}
Choices.py
from django.db import models
class WeightUnitChoices(models.TextChoices):
GRAMS = "gram", "gram"
OZ = "ounce", "ounce"
LBS = "pound", "pound"
KG = "kilogram", "kilogram"
T = "tonne", "tonne"
class VolumeUnitChoices(models.TextChoices):
ML = "milliliter", "milliliter"
L = "liter", "liter"
TSP = "teaspoon", "teaspoon"
TBPS = "tablespoon", "tablespoon"
ALL_UNIT_CHOICES = [
(None, "Select Unit"),
("Weight", WeightUnitChoices.choices),
("Volume", VolumeUnitChoices.choices),
]
def get_unit_group(unit):
"""returns the unit group to which unit belongs to"""
match unit:
case unit if unit in dict(WeightUnitChoices.choices):
return WeightUnitChoices.choices
case unit if unit in dict(VolumeUnitChoices.choices):
return VolumeUnitChoices.choices
case _:
# Return all available units
return ALL_UNIT_CHOICES
Screenshot
Related
I want to render every {{ episode.object }} in single video.html page where it works fine for {{ video.object }}. But it isn't showing anything for episode object.. The final template video.html that I want to render episode objects can be seen here https://ibb.co/K9NMXtS
I tried
{% for episode in episodes %}
{{ episode.title }}
{% endfor %}
But that didn't worked. Here is the other configurations:-
#models.py
class Video(models.Model):
title = models.CharField(max_length=100, unique=True)
slug = models.SlugField(max_length=200, unique=True)
year = models.CharField(max_length=4)
category = models.CharField(max_length=3)
trailer = models.URLField(default='')
def __str__(self):
return self.title
def get_absolute_url(self):
from django.urls import reverse
return reverse("video.html", kwargs={"slug": str(self.slug)})
class Episode(models.Model):
video = models.ForeignKey(Video, related_name='episodes', on_delete=models.CASCADE)
title = models.CharField(max_length=512)
air_date = models.DateField()
videolink = models.URLField(default='')
def __str__(self):
return self.title
# urls.py
urlpatterns = [
path('video/<slug>/', views.VideoDetail.as_view(), name='videos'),
]
# view.py
class VideoDetail(DetailView):
model = Video
template_name = 'video/video.html'
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args,**kwargs)
context['Episodes'] = Episode.objects.all()
return context
Python and Django templates are case sensitive. You use Episodes in the view, which doesn't match episodes in the template. Change one of them so that it matches (episodes is recommended for model instances in Python/Django).
Next, you are using Episode.objects.all() at the moment, which will display all episodes. If you only want the episodes for that video, then filter the queryset:
context['episodes'] = Episode.objects.filter(video=self.object)
Or you can get the same result by following the foreign key backwards:
context['episodes'] = self.object.episodes.all()
add VideoDetail, self in super tag
and while calling in template {{ episode.video.slug }}
and slug enough in url
path('video/<slug>/', views.VideoDetail.as_view(), name='videos'),
class VideoDetail(DetailView):
model = Episode
template_name = 'video/video.html'
def get_context_data(self, *args, **kwargs):
context = super(VideoDetail, self).get_context_data(*args,**kwargs)
context['Episodes'] = Episode.objects.all()
return context
I am working on a project that requires an upload of multiple images in one of the form wizard steps. The form wizard also has several models used for the wizard which I think complicates the whole process the more. Here is the relevant code:
models.py
from django.db import models
from django.contrib.auth.models import User
from location_field.models.plain import PlainLocationField
from PIL import Image
from django.core.validators import MaxValueValidator, MinValueValidator
from listing_admin_data.models import (Service, SubscriptionType, PropertySubCategory,
PropertyFeatures, VehicleModel, VehicleBodyType, VehicleFuelType,
VehicleColour, VehicleFeatures, BusinessAmenities, Currency
)
class Listing(models.Model):
listing_type_choices = [('P', 'Property'), ('V', 'Vehicle'), ('B', 'Business/Service'), ('E', 'Events')]
listing_title = models.CharField(max_length=255)
listing_type = models.CharField(choices=listing_type_choices, max_length=1, default='P')
status = models.BooleanField(default=False)
featured = models.BooleanField(default=False)
city = models.CharField(max_length=255, blank=True)
location = PlainLocationField(based_fields=['city'], zoom=7, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
expires_on = models.DateTimeField(auto_now=True)
created_by = models.ForeignKey(User,
on_delete=models.CASCADE, editable=False, null=True, blank=True
)
listing_owner = models.ForeignKey(User,
on_delete=models.CASCADE, related_name='list_owner'
)
def __str__(self):
return self.listing_title
def get_image_filename(instance, filename):
title = instance.listing.listing_title
slug = slugify(title)
return "listings_pics/%s-%s" % (slug, filename)
class ListingImages(models.Model):
listing = models.ForeignKey(Listing, on_delete=models.CASCADE)
image_url = models.ImageField(upload_to=get_image_filename,
verbose_name='Listing Images')
main_image = models.BooleanField(default=False)
class Meta:
verbose_name_plural = "Listing Images"
def __str__(self):
return f'{self.listing.listing_title} Image'
class Subscriptions(models.Model):
subscription_type = models.ForeignKey(SubscriptionType, on_delete=models.CASCADE)
subscription_date = models.DateTimeField(auto_now_add=True)
subscription_amount = models.DecimalField(max_digits=6, decimal_places=2)
subscribed_by = models.ForeignKey(User, on_delete=models.CASCADE)
duration = models.PositiveIntegerField(default=0)
listing_subscription = models.ManyToManyField(Listing)
updated_at = models.DateTimeField(auto_now=True)
status = models.BooleanField(default=False)
class Meta:
verbose_name_plural = "Subscriptions"
def __str__(self):
return f'{self.listing.listing_title} Subscription'
class Property(models.Model):
sale_hire_choices = [('S', 'Sale'), ('R', 'Rent')]
fully_furnished_choices = [('Y', 'Yes'), ('N', 'No')]
listing = models.OneToOneField(Listing, on_delete=models.CASCADE)
sub_category = models.ForeignKey(PropertySubCategory, on_delete=models.CASCADE)
for_sale_rent = models.CharField(choices=sale_hire_choices, max_length=1, default=None)
bedrooms = models.PositiveIntegerField(default=0)
bathrooms = models.PositiveIntegerField(default=0)
rooms = models.PositiveIntegerField(default=0)
land_size = models.DecimalField(max_digits=10, decimal_places=2)
available_from = models.DateField()
car_spaces = models.PositiveIntegerField(default=0)
fully_furnished = models.CharField(choices=fully_furnished_choices, max_length=1, default=None)
desc = models.TextField()
property_features = models.ManyToManyField(PropertyFeatures)
price = models.DecimalField(max_digits=15, decimal_places=2)
currency = models.ForeignKey(Currency, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
verbose_name_plural = "Properties"
def __str__(self):
return f'{self.listing.listing_title}'
The forms for this app is as follows:
forms.py
from django import forms
from .models import Listing, Property, Vehicle, Business, ListingImages
class ListingDetails(forms.ModelForm):
class Meta:
model = Listing
fields = ['listing_title', 'city', 'location']
class PropertyDetails1(forms.ModelForm):
class Meta:
model = Property
fields = ['sub_category', 'for_sale_rent', 'bedrooms', 'bathrooms',
'rooms', 'land_size', 'available_from', 'car_spaces', 'fully_furnished',
'desc', 'currency', 'price'
]
class PropertyDetails2(forms.ModelForm):
class Meta:
model = Property
fields = ['property_features']
class ListingImagesForm(forms.ModelForm):
class Meta:
model = ListingImages
fields = ['image_url']
The view that handles all this, though not yet complete as I am still researching on the best way to save the data to the database is as shown below:
views.py
from django.shortcuts import render
import os
from .forms import ListingDetails, PropertyDetails1, PropertyDetails2, ListingImagesForm
from .models import ListingImages
from formtools.wizard.views import SessionWizardView
from django.conf import settings
from django.core.files.storage import FileSystemStorage
from django.forms import modelformset_factory
from django.contrib import messages
from django.http import HttpResponseRedirect
class PropertyView(SessionWizardView):
ImageFormSet = modelformset_factory(ListingImages, form=ListingImagesForm, extra=3)
template_name = "listings/create_property.html"
formset = ImageFormSet(queryset=Images.objects.none())
form_list = [ListingDetails, PropertyDetails1, PropertyDetails2, ListingImagesForm]
file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'media'))
def done(self, form_list, **kwargs):
return render(self.request, 'done.html', {
'form_data': [form.cleaned_data for form in form_list],
})
The template that is used to handle the form fields is as below:
create_property.py
<p>Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</p>
<form action="" method="post">
{% csrf_token %}
<table>
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form }}
{% endfor %}
{% else %}
{% for field in wizard.form %}
<div class="form-group">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }}
<span class="message">{{ field.errors }}</span>
</div>
{% endfor %}
{% endif %}
</table>
{% if wizard.steps.prev %}
<div class="d-flex justify-content-around">
<button name="wizard_goto_step" type="submit" class="btn btn-primary" value="{{ wizard.steps.first }}">First Step</button>
<button name="wizard_goto_step" type="submit" class="btn btn-primary" value="{{ wizard.steps.prev }}">Previous Step</button>
</div>
{% endif %}
<div class="d-flex justify-content-end col-12 mb-30 pl-15 pr-15">
<input type="submit" value="{% trans "submit" %}"/>
</div>
</form>
I have tried to attach all the relevant information for this to work.
The main problem I am facing is that the template doesn't give room for multiple uploads, and again, after I attach a single file that is provided and try to submit, I get a field cannot be empty error message.
I am still new to Django and trying to learn as I code, but so far, not much has been documented about the form wizard and the multiple image upload issues. Most of the posts available appear to be shoddy and only uses the contact form which does not store any details on the database.
The solution is from this Project
MultipleUpload.py
from django import forms
FILE_INPUT_CONTRADICTION = object()
class ClearableMultipleFilesInput(forms.ClearableFileInput):
# Taken from:
# https://stackoverflow.com/questions/46318587/django-uploading-multiple-files-list-of-files-needed-in-cleaned-datafile#answer-46409022
def value_from_datadict(self, data, files, name):
upload = files.getlist(name) # files.get(name) in Django source
if not self.is_required and forms.CheckboxInput().value_from_datadict(
data, files, self.clear_checkbox_name(name)):
if upload:
# If the user contradicts themselves (uploads a new file AND
# checks the "clear" checkbox), we return a unique marker
# objects that FileField will turn into a ValidationError.
return FILE_INPUT_CONTRADICTION
# False signals to clear any existing value, as opposed to just None
return False
return upload
class MultipleFilesField(forms.FileField):
# Taken from:
# https://stackoverflow.com/questions/46318587/django-uploading-multiple-files-list-of-files-needed-in-cleaned-datafile#answer-46409022
widget = ClearableMultipleFilesInput
def clean(self, data, initial=None):
# If the widget got contradictory inputs, we raise a validation error
if data is FILE_INPUT_CONTRADICTION:
raise forms.ValidationError(self.error_message['contradiction'], code='contradiction')
# False means the field value should be cleared; further validation is
# not needed.
if data is False:
if not self.required:
return False
# If the field is required, clearing is not possible (the widg et
# shouldn't return False data in that case anyway). False is not
# in self.empty_value; if a False value makes it this far
# it should be validated from here on out as None (so it will be
# caught by the required check).
data = None
if not data and initial:
return initial
return data
enter code here
from django.core.files.uploadedfile import UploadedFile
from django.utils import six
from django.utils.datastructures import MultiValueDict
from formtools.wizard.storage.exceptions import NoFileStorageConfigured
from formtools.wizard.storage.base import BaseStorage
class MultiFileSessionStorage(BaseStorage):
"""
Custom session storage to handle multiple files upload.
"""
storage_name = '{}.{}'.format(__name__, 'MultiFileSessionStorage')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.prefix not in self.request.session:
self.init_data()
################################################################################################
# Helper
################################################################################################
def _get_data(self):
self.request.session.modified = True
return self.request.session[self.prefix]
def _set_data(self, value):
self.request.session[self.prefix] = value
self.request.session.modified = True
data = property(_get_data, _set_data)
################################################################################################
# formtools.wizard.storage.base.BaseStorage API overrides
################################################################################################
def reset(self):
# Store unused temporary file names in order to delete them
# at the end of the response cycle through a callback attached in
# `update_response`.
wizard_files = self.data[self.step_files_key]
for step_files in six.itervalues(wizard_files):
for file_list in six.itervalues(step_files):
for step_file in file_list:
self._tmp_files.append(step_file['tmp_name'])
self.init_data()
def get_step_files(self, step):
wizard_files = self.data[self.step_files_key].get(step, {})
if wizard_files and not self.file_storage:
raise NoFileStorageConfigured(
"You need to define 'file_storage' in your "
"wizard view in order to handle file uploads.")
files = {}
for field in wizard_files.keys():
files[field] = {}
uploaded_file_list = []
for field_dict in wizard_files.get(field, []):
field_dict = field_dict.copy()
tmp_name = field_dict.pop('tmp_name')
if(step, field, field_dict['name']) not in self._files:
self._files[(step, field, field_dict['name'])] = UploadedFile(
file=self.file_storage.open(tmp_name), **field_dict)
uploaded_file_list.append(self._files[(step, field, field_dict['name'])])
files[field] = uploaded_file_list
return MultiValueDict(files) or MultiValueDict({})
def set_step_files(self, step, files):
if files and not self.file_storage:
raise NoFileStorageConfigured(
"You need to define 'file_storage' in your "
"wizard view in order to handle file uploads.")
if step not in self.data[self.step_files_key]:
self.data[self.step_files_key][step] = {}
for field in files.keys():
self.data[self.step_files_key][step][field] = []
for field_file in files.getlist(field):
tmp_filename = self.file_storage.save(field_file.name, field_file)
file_dict = {
'tmp_name': tmp_filename,
'name': field_file.name,
'content_type': field_file.content_type,
'size': field_file.size,
'charset': field_file.charset
}
self.data[self.step_files_key][step][field].append(file_dict)
form.py
from MultipleUpload import MultipleFilesField, ClearableMultipleFilesInput
class ListingImagesForm(forms.ModelForm):
image_url = MultipleFilesField(widget=ClearableMultipleFilesInput(
attrs={'multiple': True, 'accept':'.jpg,.jpeg,.png'}), label='Files')
class Meta:
model = ListingImages
fields = ['image_url']
views.py
from MultipleUpload import MultiFileSessionStorage
class PropertyView(SessionWizardView):
storage_name = MultiFileSessionStorage.storage_name
ImageFormSet = modelformset_factory(ListingImages, form=ListingImagesForm, extra=3)
template_name = "listings/create_property.html"
formset = ImageFormSet(queryset=Images.objects.none())
form_list = [(....), ('ListingImagesForm',ListingImagesForm)]
file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'media'))
def done(self, form_list, **kwargs):
cleaned_data = self.get_cleaned_data_for_step('ListingImagesForm')
for f in cleaned_data.get('image_url',[]):
instance = ListingImages(image_url=f, .....)
instance.save()
return render(self.request, 'done.html', {
'form_data': [form.cleaned_data for form in form_list],
})
My project has many physics questions stored in its database where each of these questions belongs to a Physics' topic and a Question type.
I have two ChoiceField:
* One for topics and includes 16 topics.
* One for question type and includes two question types.
I have a submit button that is supposed to show me the results of my filtering, however, I don't know how to write the Query Sets in the views.py although I have read the Documentation but still don't know how to make one query or more to get my results.
This is the models.py
from django.db import models
from home.choices import *
# Create your models here.
class Topic(models.Model):
topic_name = models.IntegerField(
choices = question_topic_name_choices, default = 1)
def __str__(self):
return '%s' % self.topic_name
class Image (models.Model):
image_file = models.ImageField()
def __str__(self):
return '%s' % self.image_file
class Question(models.Model):
question_type = models. IntegerField(
choices = questions_type_choices, default = 1)
question_topic = models.ForeignKey( 'Topic',
on_delete=models.CASCADE,
blank=True,
null=True)
question_description = models.TextField()
question_answer = models.ForeignKey( 'Answer',
on_delete=models.CASCADE,
blank=True,
null=True)
question_image = models.ForeignKey( 'Image',
on_delete=models.CASCADE,
blank=True,
null=True)
def __str__(self):
return '%s' % self.question_type
class Answer(models.Model):
answer_description = models.TextField()
answer_image = models.ForeignKey( 'Image',
on_delete=models.CASCADE,
blank=True,
null=True)
def __str__(self):
return '%s' % self.answer_description
This is the forms.py
from django import forms
from betterforms.multiform import MultiModelForm
from .models import Topic, Image, Question, Answer
from .choices import questions_type_choices, question_topic_name_choices
class TopicForm(forms.ModelForm):
topic_name = forms.ChoiceField(
choices=question_topic_name_choices,
widget = forms.Select(
attrs = {'class': 'home-select-one'}
))
class Meta:
model = Topic
fields = ['topic_name',]
def __str__(self):
return self.fields
class QuestionForm(forms.ModelForm):
question_type = forms.ChoiceField(
choices= questions_type_choices,
widget = forms.Select(
attrs = {'class': 'home-select-two'},
))
class Meta:
model = Question
fields = ['question_type',]
def __str__(self):
return self.fields
class QuizMultiForm(MultiModelForm):
form_classes = {
'topics':TopicForm,
'questions':QuestionForm
}
This is the views.py
from django.shortcuts import render, render_to_response
from django.views.generic import CreateView, TemplateView
from home.models import Topic, Image, Question, Answer
from home.forms import QuizMultiForm
def QuizView(request):
if request.method == "POST":
form = QuizMultiForm(request.POST)
if form.is_valid():
pass
else:
form = QuizMultiForm()
return render(request, "index.html", {'form': form})
This is the index.html
{% extends 'base.html' %} {% block content %}
<form method="POST">
{% csrf_token %} {{ form.as_p }}
<button type="submit" id="home-Physics-time-button">
It is Physics Time</button>
</form>
{% endblock content %}
Any help would be great. Thank you!
i dont know what exactly you want to filter or if i understood correctly (cant add comments yet), but here is an example:
views.py
def QuizView(request):
topics = Topic.objects.filter(topic_name=1) # i dont know your choices, but i go with the set default
if request.method == "POST":
form = QuizMultiForm(request.POST)
if form.is_valid():
pass
else:
form = QuizMultiForm()
return render(request, "index.html", {'form': form, 'topics':'topics})
template part for calling now the query
{% for topic in topics %}
<h1> {{ topic.topic_name }} </h1>
{% endfor %}
explanation: you are filtering the query in your view by .filter(model_field=)
in your template you iterate trough all results (you are passing 'topics' from the view into the template by the context parameter in your curly brackets), filtered by the view
I want to retrieve the question_description answer_descritption and question_image answer_image if found in the database according to topic and question type using two ChoiceField for both: Topic and Question Type.
However, I don't know how to do that. I have seen some tutorials and gotten glimpses of what I have to do, but I am not sure how to preform the same techniques on my case because online there are not that many ChoiceField examples, except that there are general examples on how to use forms and extract data from the database.
This is the models.py
from django.db import models
from home.choices import *
# Create your models here.
class Topic(models.Model):
topic_name = models.IntegerField(
choices = question_topic_name_choices, default = 1)
def __str__(self):
return '%s' % self.topic_name
class Image (models.Model):
image_file = models.ImageField()
def __str__(self):
return '%s' % self.image_file
class Question(models.Model):
question_type = models. IntegerField(
choices = questions_type_choices, default = 1)
question_topic = models.ForeignKey( 'Topic',
on_delete=models.CASCADE,
blank=True,
null=True)
question_description = models.TextField()
question_answer = models.ForeignKey( 'Answer',
on_delete=models.CASCADE,
blank=True,
null=True)
question_image = models.ForeignKey( 'Image',
on_delete=models.CASCADE,
blank=True,
null=True)
def __str__(self):
return '%s' % self.question_type
class Answer(models.Model):
answer_description = models.TextField()
answer_image = models.ForeignKey( 'Image',
on_delete=models.CASCADE,
blank=True,
null=True)
answer_topic = models.ForeignKey( 'Topic',
on_delete=models.CASCADE,
blank=True,
null=True)
def __str__(self):
return '%s' % self.answer_description
This is the forms.py
from django import forms
from betterforms.multiform import MultiModelForm
from .models import Topic, Image, Question, Answer
from .choices import questions_type_choices, question_topic_name_choices
class TopicForm(forms.ModelForm):
topic_name = forms.ChoiceField(
choices=question_topic_name_choices,
widget = forms.Select(
attrs = {'class': 'home-select-one'}
))
class Meta:
model = Topic
fields = ['topic_name',]
def __str__(self):
return self.fields
class QuestionForm(forms.ModelForm):
question_type = forms.ChoiceField(
choices= questions_type_choices,
widget = forms.Select(
attrs = {'class': 'home-select-two'},
))
class Meta:
model = Question
fields = ['question_type',]
def __str__(self):
return self.fields
class QuizMultiForm(MultiModelForm):
form_classes = {
'topics':TopicForm,
'questions':QuestionForm
}
This is the views.py
from django.shortcuts import render, render_to_response
from django.views.generic import TemplateView
from home.models import Topic, Image, Question, Answer
from home.forms import QuizMultiForm
class QuizView(TemplateView):
template_name = 'index.html'
def get(self, request):
# What queries do I need to put here to get the question and answer's description according to the ChoiceField input
form = QuizMultiForm()
return render (request, self.template_name, {'form': form})
def post(self, request):
form = QuizMultiForm(request.POST)
if form.is_valid():
text = form.cleaned_data['topic_name', 'question_type'] # I don't know what to put here!
args = {'form': form, 'text': text}
return render (request, self.template_name, args)
This is the template:
{% extends 'base.html' %}
{% block content %}
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" id="home-Physics-time-button">It is Physics Time</button>
<h1> {{ text }} </h1>
</form>
{% endblock content %}
I would appropriate the help!
Thank you!
The cleaned_data attribute of the form, contains a dictionary that maps the name of the field with the bounded data. And from the MultiForm docs you can read:
cleaned_data
Returns an OrderedDict of the cleaned_data for each of the child forms.
Just extract data like this:
topic_name = form.cleaned_data['topics']['topic_name']
question_type = form.cleaned_data['question']['question_type']
# And so on ...
Sorry for my poor english...
I've got a Model called Habitation :
class Habitation(models.Model):
propr = models.ForeignKey(Client, related_name="proprietaire")
locat = models.ForeignKey(Client, related_name="locataire", null=True, blank=True)
etage = models.CharField(max_length=2, blank=True)
numero = models.CharField(max_length=3, blank=True)
ad1 = models.CharField(max_length=64)
ad2 = models.CharField(max_length=64, blank=True)
cp = models.CharField(max_length=5)
ville = models.CharField(max_length=32)
def get_appareils(self):
return Appareil.objects.filter(habitation=self)
def selflink(self):
if self.id:
return 'Editer' % str(self.id)
else:
return 'Indéfini'
selflink.allow_tags = True
def __unicode__(self):
return u'%s - %s %s' % (self.ad1, self.cp, self.ville)
With his edit view :
def edit(request, habitation_id):
habitation = Habitation.objects.get(pk=habitation_id)
if request.POST:
form = HabitationForm(request.POST, instance=habitation)
if form.is_valid():
form.save()
return redirect('clients')
else:
form = HabitationForm(instance=habitation)
print form.fields
return render_to_response('habitations/edit.html', {
'habitation_id': habitation_id,
'form': form,
}, context_instance=RequestContext(request))
and his template :
<table>
<form action="/habitations/edit/{{ habitation_id }}/" method="post">
{{ form }}
{% csrf_token %}
{{ form.as_table }}
</form>
</table>
Form:
from django import forms
from client import models
class HabitationForm(forms.ModelForm):
class meta:
model = models.Habitation
fields = ('propr', 'locat', 'etage', 'numero', 'ad1', 'ad2', 'cp', 'ville',)
My view (or my ModelForm) doesn't retrive any field, so no more form field.
Is anybody has any suggestion ?
The meta class name in form should be Meta not meta.
Update your form to
from django import forms
from client import models
class HabitationForm(forms.ModelForm):
class Meta: #<---- define with capital M
model = models.Habitation
fields = ('propr', 'locat', 'tegae', 'numero', 'ad1', 'ad2', 'cp', 'ville',)