Hi bit of a beginner question about using django's modelchoicefield in a form I'm building.
I just need get django to display a drop down list of ingredients in a form. I've gotten to the point where the page renders but the form does not, I was getting errors before so I am kind of perplexed at the moment. I was hoping for some guidance.
Using python 2.7.6 and django 1.6.2. If I left anything out let me know.
Thanks!
Code is below:
views:
args = {}
#add csrf sercurity
args.update(csrf(request))
args['form'] = form
return render_to_response('newMeal.html', args)
form:
from django import forms
from models import meals, ingredients, recipe
class mealForm(forms.ModelForm):
breakfast = forms.ModelChoiceField(queryset=recipe.objects.all())
# Lunch = forms.ModelChoiceField(queryset=recipe.objects.all())
# Dinner = forms.ModelChoiceField(queryset=recipe.objects.all())
class Meta:
model = meals
fields = ('Breakfast','Lunch','Dinner','servingDate')
class recipeForm(forms.ModelForm):
class Meta:
model = recipe
fields = ('Name', 'Directions')
template:
{% extends "base.html" %}
{% block content %}
<p>New Meals go here!</p>
<form action="/meals/newmeal/" method="post">{% csrf_token %}
<table class="selection">
{{form.as_table}}
<tr><td colspan="2"><input type="submit" name="submit" value="Add Meal"></td></tr>
</table>
</form>
{% endblock %}
Model;
from django.db import models
import datetime
Create your models here.
class recipe(models.Model):
Name = models.CharField(max_length=200)
Directions = models.TextField()
pub_date = models.DateTimeField(auto_now_add = True)
def __unicode__(self):
return (self.id, self.Name)
class ingredients(models.Model):
Name = models.CharField(max_length=200)
Quantity = models.IntegerField(default=0)
Units = models.CharField(max_length=10)
Recipe = models.ForeignKey(recipe)
def __unicode__(self):
return self.Name
class meals(models.Model):
Breakfast = models.CharField(max_length=200)
Lunch = models.CharField(max_length=200)
Dinner = models.CharField(max_length=200)
servingDate = models.DateTimeField('date published')
did you import the mealForm:
some thing like :from app.forms import mealForm
form is a function. so try:
args['form'] = mealForm()
Note: don't use render_to_response. it is old use render instead(so don't even need csrf)::
from django.shortcuts import render
def...(request):
....
return render(request,'newMeal.html', {'form': mealForm()})
Related
I'm discovering Django and I'm trying to develop a simple application.
I have three tables in my database :
One big table to report all the information to users and 2 tables to create drop down list on my form (but no usage of foreign keys on purpose). I need to have these two tables because statuses and areas need to be editable at all time and need to be written in the same way each time in the main table Action.
Here is my model.py :
class Status(models.Model):
id_status = models.AutoField(db_column='ID_STATUS', primary_key=True)
status = models.CharField(db_column='STATUS', max_length=50)
def __str__(self):
return self.status
class Meta:
managed = False
db_table = 'T_EAVP_STATUS'
class Area(models.Model):
id_area = models.AutoField(db_column='ID_AREA', primary_key=True)
area_name = models.CharField(db_column='AREA_NAME', max_length=50)
def __str__(self):
return self.area_name
class Meta:
managed = False
db_table = 'T_EAVP_AREA'
class Action(models.Model):
id = models.AutoField(db_column='ID', primary_key=True)
title = models.CharField(db_column='TITLE', max_length=200)
due_date = models.DateTimeField(db_column='DUE_DATE')
status = models.CharField(db_column='STATUS', max_length=50)
date_insert = models.DateTimeField(db_column='DATE_INSERT', auto_now_add=True)
emitting_area = models.CharField(db_column='EMITTING_AREA', max_length=50)
receiving_area = models.CharField(db_column='RECEIVING_AREA', max_length=50)
owner = models.CharField(db_column='OWNER', max_length=200)
def __str__(self):
return self.title
class Meta:
managed = False
db_table = 'T_EAVP_ACTION'
Here is my forms.py :
class ActionForm(forms.ModelForm):
status = forms.ModelChoiceField(queryset=Status.objects.all())
receiving_area = forms.ModelChoiceField(queryset=Area.objects.all())
emitting_area = forms.ModelChoiceField(queryset=Area.objects.all())
class Meta:
model = Action
fields = ['title', 'due_date', 'owner']
Here is my views.py :
#csrf_exempt
#xframe_options_exempt
def action_add(request):
if request.method == 'POST':
form = ActionForm(request.POST)
if form.is_valid():
action = form.save()
return redirect('action-list')
else:
form = ActionForm()
return render(request, 'polls/action_add.html', {'form': form})
Here is my HTML code :
{% extends 'polls/base.html' %}
{% block title %}{{ action.title }}{% endblock %}
{% block content %}
<h1>Ajouter une action</h1>
<form action="" method="post" class="form" novalidation>
{% csrf_token %}
{{ form }}
<br><br>
<input type="submit" class="submit" value="Créer">
</form>
{% endblock %}
I'm pretty sure I've imported all the needed libraries from Django.
Problem : When I'm running my code and I try to create a new action using my ActionForm, it is not working properly.
In the Action table, the fields that correspond to the fields filled by the drop down lists are completely empty.
Table Status and table Area contain values so the problem is not coming from here.
I've tried a lot of different things but nothing seems to work and fields are always empty in my database after the save of the form.
If someone sees a solution, I'm interested !
The solution was to add the name of the fields in form.py :
class ActionForm(forms.ModelForm): # Crée un formulaire se basant sur Action
status = forms.ModelChoiceField(queryset=Status.objects.all())
receiving_area = forms.ModelChoiceField(queryset=Area.objects.all())
emitting_area = forms.ModelChoiceField(queryset=Area.objects.all())
class Meta:
model = Action
fields = ['title', 'due_date', 'owner', 'status', 'receiving_area', 'emitting_area']
Thank you Willem Van Onsem !
I am using the django-filter module. I would like to get all objects where the year is 2011 or greater.
this is my setup ...
Actual behaviour:
When I enter a year, then I get the error message: "Enter a valid date." and the queryset is unchanged.
I tried entering a date instead of the year, but then the whole page fails with the error message Field 'None' expected a number but got datetime.date(2011, 1, 1).
The model:
class BaseInvoice(models.Model):
id = ShortUUIDField(primary_key=True)
invoive_number = models.CharField(max_length=50)
invoicing_party_ID = models.ForeignKey(InvoicingParty, on_delete=models.CASCADE)
invoice_type = models.CharField(
max_length=2,
choices=invoice_type,
default='IN'
)
realestate_id = models.ForeignKey(RealEstate, on_delete=models.CASCADE)
user_group = models.ForeignKey(Group, on_delete=models.CASCADE)
year_of_contribution = models.DateField(auto_now=False, auto_now_add=False)
start_of_period = models.DateField(auto_now=False, auto_now_add=False)
end_of_period = models.DateField(auto_now=False, auto_now_add=False)
interval = models.ForeignKey(Interval, on_delete=models.CASCADE, default=0)
currency = models.ForeignKey(Currency, on_delete=models.CASCADE, default='EUR')
class Meta:
abstract = True
class RealEstateTax(BaseInvoice):
realestate_tax = models.DecimalField(max_digits=20, decimal_places=2)
The Filter
import django_filters
from django_filters import DateFilter
from .models import *
class InvoiceFilter(django_filters.FilterSet):
billing_year = DateFilter(
label='Abrechnungsjahr',
field_name='start_of_period',
lookup_expr='year__gt'
)
class Meta:
model = RealEstateTax
fields = ['realestate_id', 'billing_year']
The view
from realestate.models import RealEstate
from django.shortcuts import render
from django.http import HttpResponse
from .models import *
from .filters import InvoiceFilter
from django.db.models import Sum
def utilities_bill(request):
realestate_tax = RealEstateTax.objects.all()
insurance_invoice = InsuranceInvoice.objects.all()
raTaxFilter = InvoiceFilter(request.GET, queryset=realestate_tax)
inFilter = InvoiceFilter(request.GET, queryset=insurance_invoice)
realestate_tax = raTaxFilter.qs
insurance_invoice = inFilter.qs
realestate_sum = realestate_tax.aggregate(Sum('realestate_tax'))['realestate_tax__sum']
print(request.GET)
context = {'realestate_tax':realestate_tax, 'insurance_invoice':insurance_invoice, 'raTaxFilter':raTaxFilter, 'realestate_sum':realestate_sum}
return render(request, 'billing/utilities.html', context)
This is the first part of the template:
{% extends 'main.html' %}
{% load static %}
{% block title %} Utilities Bill {% endblock %}
{% block content %}
<img src="{% static 'media/bojack.png' %}" style="width:100px">
<h1>Nebenkostenabrechnung</h1>
<br>
<div class="container">
<div class="row">
<form class="form-group" method="get">
{{raTaxFilter.form}}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
What I tried so far:
Entering an int | --> queryset is unchanged
Entering a date | --> Field 'None' expected a number but got datetime.date(2011, 1, 1).
tried it with lookup expressions : year, year__gte, year__gt | --> didn't work
Tested a filter directly in the view realestate_tax = realestate_tax.filter(start_of_period__year__gte=2011) | --> this works but then it is without the django-filter module and I have to build everything by hand
Many thanks in advance. I hope you can help me out.
Your billing_year is not a DateFilter, but an NumberFilter. Indeed, a year is an integer, not a date. This thus means that we can filter with a given number (for example 2011) on the year of that date:
class InvoiceFilter(django_filters.FilterSet):
billing_year = NumberFilter(
label='Abrechnungsjahr',
field_name='start_of_period',
lookup_expr='year__gt'
)
class Meta:
model = RealEstateTax
fields = ['realestate_id', 'billing_year']
I've been working on a small django website as I learn django. According to the documentation when you create a form class with a meta class that points at a model with foreign key fields, it'll render those fields as select inputs.
In my application I have 3 models, client test, and record where record carries two foreign keys, each of whom point to client and test respectively
Models.py
class Client(models.Model):
first = models.CharField(max_length=264)
last = models.CharField(max_length=264)
DOB = models.DateField()
def __str__(self):
return self.first + " " + self.last
class Test(models.Model):
test = models.CharField(max_length=264)
fee = models.DecimalField(max_digits=12, decimal_places=2)
def __str__(self):
return self.test
class Record(models.Model):
client = models.ForeignKey(Client, on_delete=models.CASCADE)
test = models.ForeignKey(Test, on_delete=models.CASCADE)
date = models.DateField()
def __str__(self):
return str(self.date) + " " + str(self.test) + " for " + str(self.client)
Form.py
class NewLabRecord(forms.ModelForm):
client = forms.ChoiceField(
label='Client ID',
widget=forms.Select(
attrs={'class': 'form-control'}))
test = forms.ChoiceField(
label='Test ID',
widget=forms.Select(
attrs={'class': 'form-control'}))
date = forms.DateField(
label='Test Date',
widget=forms.DateInput(
attrs={'class': 'form-control'}))
class Meta:
model = models.Record
fields = '__all__'
I render NewLabRecord at the top of my index view for records. The idea is to create a record and redirect back to the page (therefore seeing it in the list of records). Presently, I'm emulating class-based-views and not actually implementing it because I have not learned it yet. Nevertheless, this pattern does work for my client and test (the code is nearly identical).
Views.py
class LabRecord:
#staticmethod
def index(request):
new_record_form = forms.NewLabRecord
records = models.Record.objects.order_by('date')
print(records)
context = {
'records': records,
'new_record_form': new_record_form
}
return render(request, "layouts/lab/record/index.html", context=context)
layouts/lab/record/index.html
<div class="collapse" id="createLabRecord">
{% include 'components/lab/record/create.html' %}
</div>
components/lab/record/create.html
<!DOCTYPE html>
{% block content %}
<div class="card col-sm" style="">
<form class="form" method="post" action="{% url 'lab:create lab record' %}">
{{ new_record_form }}
{% csrf_token %}
<input type="submit" value="submit">
</form>
</div>
{% endblock %}
Now, when I go to the url for this view, /lab/records/, the view renders two select fields and an input for the date; however, the select fields are empty!
Note: I have 9 clients and 4 tests in the database!
Why is the view generating empty select fields for the foreign key fields?
In your view, you need to query your Client and Test models and put those lists into your context to make them available to your form/template.
records = models.Record.objects.order_by('date')
clients = models.Client.objects.all()
tests = models.Test.objects.all()
context = {
'records': records,
'new_record_form': new_record_form,
'clients' : clients,
'tests' : tests,
}
I have not learned the forms portion of django yet to tell you if there is something else to connect the lists to the input select fields.
Edit:
It looks like the following in your form should accomplish the desired:
class NewLabRecord(forms.ModelForm):
client = forms.ModelChoiceField(models.Client.objects.all())
test = forms.ModelChoiceField(models.Test.objects.all())
date = forms.DateField(
label='Test Date',
widget=forms.DateInput(
attrs={'class': 'form-control'}))
class Meta:
model = models.Record
fields = '__all__'
And I don't think that the changes to your view are then necessary.
I have a form that requires a URL as input. I would like to check if the URL begins with http:// or https://.
If it doesn't have one of these two 'stings' in the beginning, the form submission should give an error.
I don't have any clue on how to get started with this and could not find any info based on my limited knowledge of django and I have no clue what search terms to look up.
A basic hint would be a great help.
Thanks!
My current forms.py has a form based on a model:
class AddUrlForm(forms.ModelForm):
class Meta:
model = forwards
# fields = '__all__'
exclude = ["user", "counterA", "counterB", "shortcodeurl", "uniqueid"]
models.py:
class forwards(models.Model):
uniqueid = models.AutoField(primary_key=True)
user = models.CharField(max_length = 150)
urlA = models.CharField(verbose_name="URL Version A", max_length = 254)
counterA = models.DecimalField( max_digits=19, decimal_places=0,default=Decimal('0'))
urlB = models.CharField(verbose_name="URL Version B",max_length = 254)
counterB = models.DecimalField( max_digits=19, decimal_places=0,default=Decimal('0'))
timestamp = models.DateTimeField('date created', auto_now_add=True)
shortcodeurl = models.CharField(max_length = 254)
html segment where that shows how the form is integrated:
<form method="post">
{% csrf_token %}
{% for field in forwardform %}
<span>{{ field.label_tag }} </span>
<p style="color: black">{{ field }} </p>
{% for error in field.errors %}
<p style="color: red">{{ error }}</p>
{% endfor %}
{% endfor %}
<button class="btn btn-outline btn-xl type="submit">Generate URL</button>
</form>
views.py:
def forwardthis(request):
forwardform = AddUrlForm(request.POST or None)
if forwardform.is_valid():
forward = forwardform.save(commit=False)
forward.user = request.user.username
forward = forwardform.save()
uniqueid_local = forward.uniqueid
uniqueid_local_bytes = uniqueid_local.to_bytes((uniqueid_local.bit_length() + 7) // 8, byteorder='little')
shortcodeurl_local = urlsafe_base64_encode(uniqueid_local_bytes)
forward.shortcodeurl = shortcodeurl_local
forward.save()
return HttpResponseRedirect('/forwardthis')
query_results = forwards.objects.filter(user=request.user.username)
query_results_qty = query_results.count()
click_results = clickstats.objects.filter(user=request.user.username)
template = loader.get_template('forwardthis.html')
context = {
'forwardform': forwardform ,
'query_results':query_results,
'query_results_qty': query_results_qty
}
return HttpResponse(template.render(context,request))
You can create a validation method for each form field. def clean_FIELDNAME(). I supose the url field is shortcodeurl:
class AddUrlForm(forms.ModelForm):
def clean_shortcodeurl(self):
cleaned_data = self.clean()
url = cleaned_data.get('shortcodeurl')
if not is_valid_url(url): # You create this function
self.add_error('shortcodeurl', "The url is not valid")
return url
class Meta:
model = forwards
# fields = '__all__'
exclude = ["user", "counterA", "counterB", "shortcodeurl", "uniqueid"]
For anyone coming here in 2021.
Nowadays Django provides the tools to achieve this kind of validation.
Based on Django 3.2 documentation
from django import forms
from django.core.exceptions import ValidationError
class AddUrlForm(forms.ModelForm):
class Meta:
model = forwards
# fields = '__all__'
exclude = ["user", "counterA", "counterB", "shortcodeurl", "uniqueid"]
def clean_shortcodeurl(self):
data = self.cleaned_data['shortcodeurl']
if "my_custom_example_url" not in data:
raise ValidationError("my_custom_example_url has to be in the provided data.")
return data
Update #4:
The for loop in slider.html is currently not pulling content after the last update. Slider.html was randomized; however, I'm getting four of the same story and the urls are not going to their appropriate detailed view page anymore.
List.html has been fixed and is now random.
slider.html - This section is still wonky, (updated - 4:19 p.m.)
{% for random_article in random_articles %}
<div class="slider">
<div class="group visible">
<div class="sliderItem">
<img src="{{random_article.relatedImage}}" alt="" class="sliderPicture">
<p class="related">{{random_article.title}}</p>
</div><!-- /.sliderItem -->
</div><!-- /.group -->
</div><!-- /.slider -->
{% endfor %}
Here is the URL error when I click to detailed view:
NoReverseMatch at /last-us
Reverse for 'detailed' with arguments '()' and keyword arguments '{u'slug': ''}' not found. 1 pattern(s) tried: ['(?P<slug>\\S+)']
New culprits (for why slider.html isn't working)
urls.py
from django.conf.urls import patterns, url
from . import views
urlpatterns = patterns(
'',
url(r'^$', views.BlogIndex.as_view(), name="list"),
url(r'^(?P<slug>\S+)', views.BlogDetail.as_view(), name="detailed"),
)
views.py (updated - 4:19 p.m.)
Added context['random_slider'] = FullArticle.objects.order_by('?')[:4] but I don't think this is the right approach. So that I can get four different articles vs. four of the same article randomized.
from django.views import generic
from . import models
from .models import FullArticle
# Create your views here.
class BlogIndex(generic.ListView):
queryset = models.FullArticle.objects.published()
template_name = "list.html"
def get_context_data(self, **kwargs):
context = super(BlogIndex, self).get_context_data(**kwargs)
context['random_article'] = FullArticle.objects.order_by('?').first()
return context
class BlogDetail(generic.DetailView):
model = models.FullArticle
template_name = "detailed.html"
def get_context_data(self, **kwargs):
context = super(BlogDetail, self).get_context_data(**kwargs)
context['object_list'] = models.FullArticle.objects.published()
return context
def get_context_data(self, **kwargs):
context = super(BlogDetail, self).get_context_data(**kwargs)
context['random_articles'] = FullArticle.objects.exclude(
pk=self.get_object().pk
).order_by('?')[:4]
return context
Original Problem
I'm using FullArticle.objects.order_by('?').first() to get a random article from my database, but it's currently giving the same article when I refresh the page. There is probably something missing from my models, view or how I'm calling it (using slice) in list.html or slider.html that is causing the problem.
The two parts I'm looking to make random on page load:
list.html (changed so that it's {{random_article.}} ) - This section of the problem is fixed.
<div class="mainContent clearfix">
<div class="wrapper">
<h1>Top 10 Video Games</h1>
{% for article in object_list|slice:":1" %}
<p class="date">{{article.pubDate|date:"l, F j, Y" }}</p> | <p class="author">{{article.author}}</p>
<img src="{{article.heroImage}}" alt="" class="mediumImage">
<p class="caption">{{article.body|truncatewords:"80"}}</p>
{% endfor %}
models.py
from django.db import models
from django.core.urlresolvers import reverse
# Create your models here.
class FullArticleQuerySet(models.QuerySet):
def published(self):
return self.filter(publish=True)
class FullArticle(models.Model):
title = models.CharField(max_length=150)
author = models.CharField(max_length=150)
slug = models.SlugField(max_length=200, unique=True)
pubDate = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
category = models.CharField(max_length=150)
heroImage = models.CharField(max_length=250, blank=True)
relatedImage = models.CharField(max_length=250, blank=True)
body = models.TextField()
publish = models.BooleanField(default=True)
gameRank = models.CharField(max_length=150, blank=True, null=True)
objects = FullArticleQuerySet.as_manager()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("FullArticle_detailed", kwargs={"slug": self.slug})
class Meta:
verbose_name = "Blog entry"
verbose_name_plural = "Blog Entries"
ordering = ["-pubDate"]
The problem is that you are setting the value of a class attribute at "compile time" and not each time the view is called. Instead, you could do:
class BlogIndex(generic.ListView):
queryset = models.FullArticle.objects.published()
template_name = "list.html"
def random_article(self):
return = FullArticle.objects.order_by('?').first()
Or:
class BlogIndex(generic.ListView):
queryset = models.FullArticle.objects.published()
template_name = "list.html"
def get_context_data(self, **kwargs):
context = super(BlogIndex, self).get_context_data(**kwargs)
context['random_article'] = FullArticle.objects.order_by('?').first()
return context
[update]
In list html, I only need one random article. In slider.html, I need four random articles, would I just tack on FullArticle.objects.order_by('?')[:4] somewhere in that def get_context_data snippet?
Yes. Make it plural in the view (don't forget to exclude the main article from the side list):
class BlogDetail(generic.DetailView):
model = models.FullArticle
template_name = "detailed.html"
def get_context_data(self, **kwargs):
context = super(BlogDetail, self).get_context_data(**kwargs)
context['random_articles'] = FullArticle.objects.exclude(
pk=self.get_object().pk
).order_by('?')[:4]
return context
At the template, do:
{% for random_article in random_articles %}
<div class="sliderItem">
<img src="{{random_article.relatedImage}}" alt="" class="sliderPicture">
<p class="related">{{random_article.title}}</p>
</div><!-- /.sliderItem -->
{% endfor %}
The generic listview just passes an object_list as context based on the queryset. In your case it means you have to either change the value of queryset in your view or override the get_context_data method and add your random item to it.
https://docs.djangoproject.com/en/dev/ref/class-based-views/mixins-multiple-object/#django.views.generic.list.MultipleObjectMixin.get_context_data