EDIT: I have since solved the problem (with help from stackoverflow and IRC, but I have to wait two days before I can mark this question as solved. See my reply below to see the solution!
I am currently working an a 'IMDB' clone for me and my friends to use. The idea is that each of us gets an account and can either submit new movies or vote on already submitted ones. These movies are sorted by rating and users can access the detail view to see the individual reviews.
To achieve this I'm using foreign keys in my models.py - one for the movie entries (with information like director, title, etc) and one for the individual votes. The latter one uses foreign keys to fetch the movies title and the user that submitted the review.
However when testing the form that submits reviews I encountered the 'NOT NULL constraint failed: list_vote.voter_id_id' error. When browsing through the error page I discovered that the foreignkey values are not submitted to the database
params: [None, None, 9.0]
query: ('INSERT INTO "list_vote" ("movie_id_id", "voter_id_id", "rating") VALUES (?, ' '?, ?)')
self <django.db.backends.sqlite3.base.SQLiteCursorWrapper object at 0x7f77b8907dc8>
Yet, when I browse the error page for the local vars at the moment of the 'post.save()' command the missing variables seem to be there
entry: <Entry: Starwars 4>
form: <VoteForm bound=True, valid=True, fields=(rating)>
movie_id: 'Starwars 4'
post: <Vote: Vote object>
request: <WSGIRequest: POST '/vote/starwars-4/'>
slug: 'starwars-4'
voter_id : <SimpleLazyObject: <User: admin>>
If I add the ' movie_id' and ' user_id' values to my modelform (in the 'fields' variable) the form actually works. (though this is not what I want, as there could potentially be hundreds of movies in this database, making selection rather difficult. And right now, any user can pretend to be anyone)
I'm probably missing something very obvious, but for the life of me I cannot figure out what I'm doing wrong. The models, forms etc are based on both the Django_girls tutorial (where they work) and the 'Hello WebApp' book (though foreign keys are not used in this book)
Does anyone know what I'm doing wrong here?
These are the models used:
class Entry(models.Model):
movie = models.CharField(max_length=255)
director = models.CharField(max_length=255)
total_votes = models.IntegerField(default=0)
rating = models.FloatField(default=0)
length = models.IntegerField(default=0)
year = models.IntegerField(default=0)
added_by = models.ForeignKey(User)
slug = models.SlugField(unique=True)
def __str__(self):
return self.movie
########################
Vote-model:
########################
class Vote(models.Model):
class Meta:
unique_together = (('voter_id','movie_id'),)
movie_id = models.ForeignKey(Entry)
voter_id = models.ForeignKey(User)
rating = models.FloatField(validators=[MinValueValidator(0), MaxValueValidator(10)])
This is my form.py:
class VoteForm(ModelForm):
class Meta:
model = Vote
fields = ('rating',)
This is the relevant function form the view.py:
def lets_vote(request, slug):
entry = Entry.objects.get(slug=slug)
if request.method == "POST":
form = VoteForm(request.POST)
if form.is_valid():
voter_id = request.user
movie_id = Entry.objects.get(slug=slug).movie
post = form.save(commit=False)
post.save()
return redirect('/movies/')
else:
form = VoteForm()
return render(request, 'list/lets_vote.html', {'entry':entry, 'form': form})
Last but not least: the voteform from the html page:
<form role="form" action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
{% endblock %}
I posted this question on the Django IRC as well and somebody there helped me solve the problem!
I originally made two mistakes:
I added a '_id' in my models. This is already done so automatically, so the variables in my database were actually called 'movie_id_id'. I've since removed the superfluous '_id'.
The '.movie' in the following line was unnecessary.
post.movie = Entry.objects.get(slug=slug).movie
The correct, working view is as follows:
def lets_vote(request, slug):
entry = Entry.objects.get(slug=slug)
form = VoteForm(request.POST)
if request.method == "POST":
if form.is_valid():
post = form.save(commit=False)
post.voter = request.user
post.movie = Entry.objects.get(slug=slug)
post.save()
return redirect('/movies')
I only needed the actual instance, but the rest of #Alasdair his suggestions were correct.
Thank you #Alasdair and Flobin from IRC!
Related
Sorry I have a really basic question while learning Django and could not find an easy answer.
My model is :
class Entry(models.Model):
name = models.CharField(max_length=200)
type = models.CharField(max_length= 200)
date = models.DateTimeField(auto_now= False, auto_now_add=True)
updated = models.DateTimeField(auto_now= True, auto_now_add= False)
description = models.TextField()
And so my general form implementation is :
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
fields = ['name','type', 'description']
views:
def add(request):
if request.method == 'POST':
form = EntryForm(request.POST)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
return HttpResponseRedirect('/')
else:
form = EntryForm()
return render(request, "form.html", {'form': form})
I want to add a quick fill button next to add button (that calls above view ) where the name and type is statically filled in the object and only textbox appears for description field.
I could not find a way to statically assign the values to my field in Django.
I had tried creating a different HTML file ( quickform.html) but {{form.as_p}} will put all the fields.
my forms.html is
<form method="POST">
{% csrf_token %}
{{form.as_p}}
<button class="btn btn-success" type='submit'>Submit</button>
</form>
what would be the best way to add a quick link to my index page where the name ( is auto-filled to the "general"+str(id)) and type is auto-filled to "general") is auto-filled and does not appear in the form page
In my opinion the easiest way to do such a thing is to add on click event to your second button then write a simple javascript function the fills the elements you want with static values.
I have a model like this:
class PersonneTravel(models.Model):
personne = models.ForeignKey(Personne, verbose_name=_(u'Person'))
travel = models.TextField(null=True, blank=True, verbose_name=_(u'Travel'))
date_start = models.DateTimeField(default=None, null=True, blank=True,
editable=True, verbose_name=_(u"Start"))
date_end = models.DateTimeField(default=None, null=True, blank=True,
editable=True, verbose_name=_(u"End"))
comments = models.TextField(null=True, blank=True,
verbose_name=_(u'Comments'))
I display a list of all the travels of one person.
What I wanted to do is to give the possibility to online-edit any of the travels, or to add one, into one view.
Here's how I did:
create a Form: class PersonneTravelForm with all fields
create a view class IndexView(LoginRequiredMixin, generic.FormView):
overload get_context_data where I create all those forms like this:
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
context['travels'] = []
for ptf in PersonneTravel.objects.filter(
personne__user=self.request.user,
):
context['travels'].append({
'obj': ptf,
'form': PersonneTravelForm({
'pk': ptf.pk,
'travel': ptf.travel.value,
'date_start': ptf.date_start,
'date_end': ptf.date_end,
'comments': ptf.comments,
})
})
from there in the template, I make a loop:
{% for v in travels %}
{% with v.form as form %}
{% include "my_home/travels/travel_form.html" %}
{% endwith %}{# form #}
{% endfor %}
and in the file travel_form.html I display the form
I've added a bit of javascript with a button "edit" for each form and we the user clicks on it, I slideDown() the form
When the users clicks "update" to update one travel, the corresponding form is sent as a POST. In the form code, I've created all filters in the form def clean_XXXX(self): with all fields properly "cleaned"
In the view, I analyze the fields in def form_valid(self, form):
if there's a pk in the fields it's for update or delete
if all other fields are None I delete
if any field is not None then I update
if there's no pk it's for add a new record, I add one
My big and only problem here is when there's an error: if I try to add an error while checking the form, and there's a problem I do this, for example:
def form_valid(self, form):
fc = form.cleaned_data
d_start = fc['date_start']
d_end = fc['date_end']
comments = fc.get('comments', None)
if d_end and d_start:
if d_end > datetime.datetime.now().date() > d_start:
form.add_error('date_start', _(u"Can't start in the past..."))
form.add_error('date_end', _(u"...and end in the future!"))
return super(IndexView, self).form_invalid(form)
Everything seems fine... except that the error field goes always into the "add new" blank form, never to the good "edit" form. How would you handle this?
You need Django Formsets for this purpose, when you are handling list of forms of a same type. Formset gives you built in features to create new records as well as delete and update older ones.
I am struggling a bit with my Django forms. When I call my form site, always validation errors appear (this field is required). I'd prefer to see this message after clicking the submit button, if a field is not filled like a javascript function would do. In addition I'm using regex for validation, which is working fine.
I am working with CVBs. Here is some code:
models.py
class Institute(models.Model):
name = models.CharField(max_length=50)
timestamp = models.DateTimeField(auto_now_add=True)
views.py
class InstituteCreate(CreateView):
model = Institute
form_class = InstituteForm
success_url = reverse_lazy('institute_list')
forms.py
class InstituteForm(forms.ModelForm):
name= forms.CharField(error_messages={'required': 'Own Error Text'}, validators=[RegexValidator(regex='^[a-zA-ZäüößÄÜÖ]*$', message='forbidden string', code='string_invalid')])
class Meta:
model = Institute
fields = ['name']
Hope someone has an idea on how to fix it.
edit1:
my template is quite simple
{% block pagetitle %}Institutes{%endblock %}
{% block content %}
<form class="form-horizontal" name="form_group" method="post">
{% csrf_token %}
<div>
{{ form.as_p }}
</div>
<input class="btn btn-primary" type="submit" value="click me" />
</form>
{% endblock %}
and my url config:
urlpatterns = patterns('',
url(r'^institute_create/$', views.InstituteCreate.as_view(), name='institute_create'),
)
I'm new to Django development so i'll try to explain the problem more detailed:
On my website, when i open the link www.exampleurl.com/institute_create my form is shown. Then i see the field where i have to enter the name for the institute. Above this field the text "this field is required" is displayed. But i don't want to see this, until i try to submit an empty form.
When i enter some text which doesnt match and i press submit button the error text field changes its message to forbidden string as expected.
Unless you're using a POST request to your view, form validation won't be triggered. There's likely an error somewhere else in your code, however, there are couple of things about your code that you'll want to address:
Classes in Python should always begin with an upper-case letter and follow the CapWords convention:
class Institute(models.Model):
name = models.CharField(max_length=50)
# just use the built-in `auto_now_add` argument
timestamp = models.DateTimeField(auto_now_add=True)
class InstituteCreate(CreateView):
model = Institute
form_class = InstituteForm
success_url = reverse_lazy('institute_list')
class InstituteForm(forms.ModelForm):
# All Django model/form fields are required by default, so you
# can drop the `required=True` here
name= forms.CharField(validators=[RegexValidator(regex='^[a-zA-ZäüößÄÜÖ]*$',
message='forbidden string', code='string_invalid')])
class Meta:
model = Institute
fields = ['name']
Otherwise, it's impossible to tell the difference between the class definition and an instance of the class, and you're a lot less likely to run into collisions.
Just out of curiosity, are you seeing in-browser HTML5 validation errors versus errors from Django? If you can add your template code to your question it might help.
I know this is a very old question, but I don't see it answered. I am a beginner in django too and I was following the Django tutorial when I faced the same issue.
I resolved it this way:
if 'voteButton' in request.POST:
context = {
'question': question,
'error_message': "You didn't select a choice"
}
return render(request, 'polls/details.html', context)
elif:
# do something else. Display error message
voteButton is the name of the 'submit' button in your form. Hope this helps! Please do let me know if this approach is wrong.
As Brandon mentioned, your form gets validated on a POST request. So ensure that during the first visit of the page, the Form doesn't get bound to a POST request.
For example, don't do this :
def register(request):
form = RegistrationForm(request.POST)
if request.method == 'POST':
if form.is_valid():
# Do something
return render(request, 'register.html', {'form': form})
You should bind the form to a POST request only if the page is accessed via a POST request. This should help:
def register(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
# DO something
else :
form = RegistrationForm()
return render(request, 'register.html', {'form': form})
I'm having really hard time trying to make this code work. I'm using Python 2.7 and Django 1.3
When I try to submit the form, it leads me to the error page, like the form has something wrong.
I have a model class with an image field:
class Livro (models.Model):
Titulo = models.CharField(max_length=200)
Autor = models.CharField(max_length=200)
Genero = models.CharField(max_length=100)
Editora = models.CharField(max_length=200)
Capa = models.ImageField(upload_to='media', blank=True, null=True)
ISBN = models.CharField(max_length=200)
Serie = models.CharField(max_length=200)
Data = models.DateField()
Tipocapa = models.CharField(max_length=100)
Lingua = models.ForeignKey(PropObra,'Lingua', related_name="lingualivro")
def __unicode__(self):
return self.Titulo
This is the view I have implemented:
def salvalivro(request):
if request.method == 'POST':
form = LivroForm(request.POST, request.FILES)
if form.is_valid():
form = LivroForm()
if not form.is_valid():
return HttpResponseRedirect('/erro/')
return render_to_response('salvalivro.html', {'form': form,}, context_instance=RequestContext(request))
And this is the code I have inside the template:
<form enctype="multipart/form-data" method="POST" action="/salvalivro/" >{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Salvar" />
</form>
Maybe the error is in front of my nose, but I've been working on this for about three days and had no result. I've read Django's documentation and many other questions around StackOverflow, but still got nothing. I really need some help...
Your view code doesn't make a whole lot of sense. You're creating a new instance of the form after checking that the previously created one is valid, then redirecting to an error page if it's not. So, your code looks to me like it's working as expected as described in your question.
Try this view instead (assumes Django 1.3):
def salvalivro(request):
form = LivroForm(request.POST or None, request.FILES or None)
if request.method == 'POST':
if form.is_valid():
form.save() #or whatever else you need
return render(request, 'salvalivro.html', {'form': form})
Your view is very odd. If it's a post, you instantiate the form using the post data. Then, you check it's valid: then you re-instantiate the form with no data and check if it isn't valid! Of course, at that point it can't ever be valid, because the second instantiation has no data. So, naturally, it always redirects - but again, because you're redirecting to a different view, you'll never see the actual error messages generated by the form.
You should look more closely at the standard documentation on using a form in a view - it has the exact pattern you should follow.
I am building a webapp which will be used by a company to carry out their daily operations. Things like sending invoices, tracking accounts receivable, tracking inventory (and therefore products). I have several models set up in my various apps to handle the different parts of the web-app. I will also be setting up permissions so that managers can edit more fields than, say, an office assistant.
This brings me to my question. How can I show all fields of a model and have some that can be edited and some that cannot be edited, and still save the model instance?
For example, I have a systems model for tracking systems (we install irrigation systems). The system ID is the primary key, and it is important for the user to see. However, they cannot change that ID since it would mess things up. Now, I have a view for displaying my models via a form using the "form.as_table". This is efficient, but merely spits out all the model fields with input fields filling in the values stored for that model instance. This includes the systemID field which should not be editable.
Because I don't want the user to edit the systemID field, I tried making it just a label within the html form, but django complains. Here's some code:
my model (not all of it, but some of it):
class System(models.Model):
systemID = models.CharField(max_length=10, primary_key=True, verbose_name = 'System ID')
systemOwner = models.ForeignKey (System_Owner)
installDate = models.DateField()
projectManager = models.ForeignKey(Employee, blank=True, null=True)
#more fields....
Then, my view for a specific model instance:
def system_details(request, systemID):
if request.method == 'POST':
sysEdit = System.objects.get(pk=systemID)
form = System_Form(request.POST, instance=sysEdit)
if form.is_valid():
form.save()
return HttpResponseRedirect('/systems/')
else:
sysView = System.objects.get(pk=systemID)
form = System_Form(instance=sysView)
return render_to_response('pages/systems/system_details.html', {'form': form}, context_instance=RequestContext(request))
Now the html page which displays the form:
<form action="" method="POST">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" value="Save Changes">
<input type="button" value="Cancel Changes" onclick="window.location.href='/systems/'">
</form>
So, what I am thinking of doing is having two functions for the html. One is a form for displaying only those fields the user can edit, and the other is for just displaying the content of the field (the systemID). Then, in the view, when I want to save the changes the user made, I would do:
sysValues = System.objects.get(pk=SystemID)
form.save(commit = false)
form.pk = sysValues.sysValues.pk (or whatever the code is to assign the sysValues.pk to form.pk)
Is there an easier way to do this or would this be the best?
Thanks
One thing you can do is exclude the field you don't need in your form:
class System_Form(forms.ModelForm):
class Meta:
exclude = ('systemID',)
The other is to use read-only fields: http://docs.djangoproject.com/en/1.3/ref/contrib/admin/#django.contrib.admin.ModelAdmin.readonly_fields as #DTing suggessted
To make a field read only you can set the widget readonly attribute to True.
using your example:
class System_Form(ModelForm):
def __init__(self, *args, **kwargs):
super(System_Form, self).__init__(*args, **kwargs)
self.fields['systemID'].widget.attrs['readonly'] = True
class Meta:
model = System
or exclude the fields using exclude or fields in the class Meta of your form and display it in your template if desired like so:
forms.py
class System_Form(ModelForms):
class Meta:
model = System
exclude = ('systemID',)
views.py
def some_view(request, system_id):
system = System.objects.get(pk=system_id)
if request.method == 'POST':
form = System_Form(request.POST, instance=system)
if form.is_valid():
form.save()
return HttpResponse('Success')
else:
form = System_Form(instance=system)
context = { 'system':system,
'form':form, }
return render_to_response('some_template.html', context,
context_instance=RequestContext(request))
some_template.html
<p>make changes for {{ system }} with ID {{ system.systemID }}</p>
<form method='post'>
{{ form.as_p }}
<input type='submit' value='Submit'>
</form>