Hidden validation form - Django - django

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.

Related

Passing data to form (from POST and url)

I'm not sure how to pass data from a given object (profile) and from form (request.POST) to one model (many to many relation).
I have two models: Profile and Keyword. Users can define many profiles (for different subjects) and in each profile they can define many keywords (used later by a crawler). It is possible that the same keyword is in many profiles, and one profile can have many keywords.
Now, I have a view adding new profile to user, and in next step I want to add view adding keyword/keywords to this particular profile.
I'm passing a parameter foreign key - profile_id - via url, and I have build form from my model Keyword. Now I have problem with passing both of them to my function.
models.py
class Profiles (models.Model):
id_profile = models.AutoField(primary_key=True)
user_id = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=45)
description = models.TextField(max_length=120, default=' ')
class Keywords (models.Model):
id_keyword = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
id_profile = models.ManyToManyField(Profiles)
template
<button class="btn btn-outline-danger" type="submit"> new keyword </button>
urls.py
path('profile_detail/<int:pk>/', users_view.ProfileDetailView.as_view(), name = 'profile_detail'),
path('new_keyword/<profile_id>/', users_view.NewKeyword, name = 'new_keyword'),
views.py
def newKeyword(request):
form = NewKeyword(request.POST)
if request.method == 'POST':
form.is_valid()
form.save()
return redirect('profile')
return render(request, 'users/new_keyword.html', {'form': form})
Now I have
__init__() got an unexpected keyword argument 'profile_id'
I understand that I have to somehow overwrite init() to accept profile_id, but I'm not sure how.
ok, thank you for you answer. I have changed my code, but now I have different problem:The Keywords could not be created because the data didn't validate.
def newKeyword(request):
context = {}
context['id_profile'] = request.POST.get('id_profile', None)
form = NewKeyword(request.POST or None)
if request.method == 'POST':
form.is_valid()
obj = form.save(commit=False)
obj.save()
obj.id_profile.add(context['id_profile'])
obj.save()
return redirect('profile')
return render(request, 'users/new_keyword.html', {'form': form})
and template in previous page:
<form method="POST" name="newkeyword" value='keyword'>
<fieldset class="form-group">
<legend class="border-bottom mb-4"> New keyword </legend>
{{ form | crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-danger" type="submit">create</button>
</div>
</form>
Looks like after clicking a button on page profile (in order to go to view: creating keyword) I use method POST to send parameter, hence my 'If statement' runs...
You are trying to retrieve GET data.
def newKeyword(request, profile_id):
and you can retrieve that parameter with this way but for POST data,
you can use;
request.GET.get('profile_id') # GET method
request.POST.get('profile_id') # POST method
and you don't need to edit url for this.
related question:
Passing variable from django template to view

quick fill forms in django

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.

Django foreign key form save

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!

Disable validation when calling form for the first time

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})

How to edit/add objects using the same django form?

Hey, I've searched around to do this (particularly this Q: Django edit form based on add form?) but I just can't get it to work.
The problem I'm having is that the form always creates a new object, instead of modifying the existing one.
This is my code:
def new_task(request, task_id=None):
if task_id is not None:
task = Task.objects.get(pk=task_id)
else:
task = Task()
if request.method == 'POST': # If the form has been submitted...
form = TaskForm(request.POST, instance=task)
if form.is_valid():
form.save();
return tasks(request, 'Task #%s created successfully.' % (task.id))
else:
form = TaskForm(instance=task)
return custom_render('user/new_task.html',
{'form': form},
request);
Any clues on what I'm missing? Thanks
Edit: added form definitions.
class TaskForm(ModelForm):
description = CharField(max_length = 1500,
widget= forms.Textarea(attrs={'class':'task-description'}),
required=True)
class Meta:
model = Task
Ok, after a nice night of debugging i found out what the problem was. Quite stupid actually.
The problem was that on submit, task_id was None.
I worked it out by doing:
On the form I added: <form action="{% url App.myapp.views.new_task **task_id** %}"
On my views I added:
return custom_render('user/new_task.html',
{'form': form, 'submit': submit, 'task_id':task.id},
request)
That was it. A newbie mistake. If someone out there knows a nicer way I'm open to suggestions.