I apologize for my confusing title but I hope the code explains it better.
In my views.py file I have the follow view
def create_view(request):
context = {}
form = CreateForm(request.POST)
if request.method == "POST":
if form.is_valid():
instance = form.save(commit=False)
instance.author = request.user
instance.save()
instance.author.profile.participating_in = Post.objects.get(
title=instance.title
)
instance.save()
print(instance.author.profile.participating_in)
context["form"] = form
return render(request, "post/post_form.html", context)
when I print out the value of instance.author.profile.participating_in it shows up in my terminal however when I check the admin page it doesnt update at all. I'm sure I messed up somewhere silly but I cant seem to find it. Thanks!
participating_in is the profile model field, but you are not calling the save() method for profile anywhere.
You have to do it like the following:
profile = instance.author.profile
profile.participating_in = Post.objects.get(title=instance.title)
profile.save()
If participating_in is ManyToManyField then we can do it like this:
post = Post.objects.get(title=instance.title)
instance.author.profile.participating_in.add(post)
Note that add(), create(), remove(), clear(), and set() all
apply database changes immediately for all types of related fields. In
other words, there is no need to call save() on either end of the
relationship.
Look at Related objects reference
Related
I am using formset to input my data into the database but for some reason it just doesn't validate, whenever I test in the terminal and call the .is_valid() It just returns false no matter what I try. Here's the code in my views.py and forms.py . Any help will be much appreciated!
# Advanced Subjects (Advanced Biology)
def form_5_entry_biology_view(self, request):
current_teacher = User.objects.get(email=request.user.email)
logged_school = current_teacher.school_number
students_involved = User.objects.get(school_number=logged_school).teacher.all()
data = {"student_name": students_involved}
formset_data = AdvancedStudents.objects.filter(class_studying="Form V", combination="PCB")
student_formset = formset_factory(AdvancedBiologyForm, extra=0)
initial = []
for element in formset_data:
initial.append({"student_name": element})
formset = student_formset(request.POST or None, initial=initial)
print(formset.is_valid())
context = {
"students": students_involved,
"formset": formset,
"class_of_students": "Form V",
"subject_name": "Advanced Biology",
}
return render(request, "analyzer/marks_entry/marks_entry_page.html", context)
And here is my forms.py
class AdvancedBiologyForm(forms.ModelForm):
student_name = forms.CharField()
class Meta:
model = ResultsALevel
fields = ('student_name', 'advanced_biology_1', 'advanced_biology_2',
'advanced_biology_3',)
Before using request.POST and is_valid() you probably want to check if there actually is a post request or if the page is just viewed:
def form_5_entry_biology_view(self, request):
current_teacher = User.objects.get(email=request.user.email)
logged_school = current_teacher.school_number
students_involved = User.objects.get(school_number=logged_school).teacher.all()
data = {"student_name": students_involved}
formset_data = AdvancedStudents.objects.filter(class_studying="Form V", combination="PCB")
# Here you are creating the formset using the model
student_formset = formset_factory(AdvancedBiologyForm, extra=0)
# Here you are generating your initial data
initial = []
for element in formset_data:
initial.append({"student_name": element})
# Here you are using the initial data to create pre-populated
# forms with it using the formset.
# These forms will be displayed when the page loads.
formset = student_formset(initial=initial)
context = {
"students": students_involved,
"formset": formset,
"class_of_students": "Form V",
"subject_name": "Advanced Biology",
}
# But if the user hits the "submit"-Button...
if request.method == 'POST':
# ... you don't want to have the formset with your
# initial data. Instead you want the entries by the user
# which are transmitted in request.POST to populate the
# formset forms.
formset = student_formset(request.POST or None)
# Now you can validate the formset with the fields which
# got input the the user; not the "initial" data like in
# your original code
if formset.is_valid():
# This runs formset validation.
# You can define your own formset validations like
# you would for models/forms.
for form in formset:
# And / Alternatively:
# you could in theory also add another "if form.is_valid():" in here
# This would trigger any validation on the
# model/form; not the validators on the formset.
form.save()
return HttpResponseRedirect(...
return render(request, "analyzer/marks_entry/marks_entry_page.html", context)
Otherwise you might call is_valid() on an unbound form.
From Django docs:
If the form is submitted using a POST request, the view will once again create a form instance and populate it with data from the request: form = NameForm(request.POST) This is called “binding data to the form” (it is now a bound form).
Basically if a form is empty it's unbound, and if it's populated with data it gets bound after POST. When you open a page and immediately try "is_valid()" it will basically always be false as you are checking if an empty form is valid; which it likely never is.
To point out the error:
formset = student_formset(request.POST or None, initial=initial)
print(formset.is_valid())
This is not valid. Because initial values are not equal to populating a form field with "real" values. So what happens is that it tries to populate the fields in the form with request.POST or None.
But you don't have a if request.method == 'POST': condition. So your code will just run through before hitting the last line of code which is the return statement displaying the page.
This means that your code validates request.POST or None before the user even saw the page. So there is no way a user could have already entered data and hit submit. Which means there is no POST-request, so it always turns None. So you are basically calling is_valid() on a form that has no field values in it which leads to the validation failing.
EDIT 1: I just noticed in your forms.py you have written:
fields = ('student_name', 'advanced_biology_1', 'advanced_biology_2',
'advanced_biology_3',)
This should be a list instead:
fields = ['student_name', 'advanced_biology_1', 'advanced_biology_2',
'advanced_biology_3',]
EDIT 2: fixed wrong variable name
EDIT 3: added extensive comments to clarify what's happening in the code
EDIT 4: pointed out cause of problem more distinctly.
I want to 'attach' a manytomany relationship on form submit.
The example is the classic blog tags-post relation: a post can have multiple tags related
In django-admin it works, but i can't figure how to do in views...
my code:
def add_post(request):
if request.method == 'POST':
form = PostForm(data=request.POST)
if form.is_valid():
model_instance = form.save(commit=False)
model_instance.author = request.user
newentry = model_instance.save()
#some magic needed here, this will never works
for tag in model_instance.tags.all():
t = Tag.objects.get_or_create(author=request.user, title=tag.title, slug=slugify(tag.title))
model_instance.tags.add(t)
#end of magic
return HttpResponseRedirect("/blog/")
else:
form = PostForm()
return render_to_response(
'blog/add_post.html',
{'form' : form },
context_instance=RequestContext(request))
I'm using the automatic tokenization from select2.js in the template, so i can to add tags "on the fly", but i'm not sure how to handle it with django...
newentry = model_instance.save()
The save() method on a model instance will not return anything, unlike the same method on a form. You should reuse model_instance instead of using newentry.
I'd also take a look at get_or_create() to create the tag if it doesn't exist already.
I have a blog where I have a ManyToManyField between posts and projects, and I select the projects to create a relationship to in the post form.
When you use commit=False on form.save(), you need to use form.save_m2m() after the form is saved using post.save().
This is what part of my view function looks like:
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
# This saves the project relation
form.save_m2m()
For your usage, I would try moving the model_instance.save() and form.save_m2m() after your magic.
Another thing about get_or_create() is that it returns a tuple with the object that was gotten or created, and a boolean in regards to whether it created the object. So you need to only pass model_instance.tags.add() the created object.
form = PostForm(data=request.POST)
if form.is_valid():
model_instance = form.save(commit=False)
model_instance.author = request.user
#some magic needed here, this will never works
for tag in model_instance.tags.all():
t, created = Tag.objects.get_or_create(author=request.user, title=tag.title, slug=slugify(tag.title))
model_instance.tags.add(t)
#end of magic
model_instance.save()
form.save_m2m()
As a database programmer (with a little django experience) I would argue that you should normalize your models. IE, add a third table (call it something like Post Tags) and set it up as an intermediary between posts and tags. So the relationship from Post to Post Tags would be 1:M and the relationship from Tags to Post Tags would be 1:M
I have a form that displays a drop down for a foreignkey field. The form saves the users selection, however I want the view to update the database entry rather than inserting a new one. Whats the best way to go about this?
Current function:
def formset_valid(self, formset):
self.object = formset.save()
self.object.save()
return HttpResponseRedirect(self.get_success_url())
I have tried something like this:
d = RevisionDefaultType.objects.get(id=1)
n = RevisionSettings.objects.get(self.object)
d.defaultrevisiontype = n
d.save()
But it throws an error that the data being updated is not an instance.
I managed to tweak the update example from https://docs.djangoproject.com/en/dev/topics/db/queries/#saving-foreignkey-and-manytomanyfield-fields. This seems to be working as desired.
def formset_valid(self, formset):
self.object = formset.save(commit=False)
revdefault = RevisionDefaultType.objects.get(pk=1)
revget = RevisionSettings.objects.get(global_revision_type=self.object)
revdefault.defaultrevisiontype = revget
revdefault.save()
return HttpResponseRedirect(self.get_success_url())
Thanks again for the help.
You must add force_update=True to your save() function.
For more info about How Django knows to UPDATE vs. INSERT see this link in django's documentation.
My view calls some backend classes which need some user input. When user input is required, i halt processing and store the questions into the session - request.session['questions']. request.session['questions'] is a list of dictionaries. e.g.
request.session['question'] = []
request.session['question'].append({'question' : 'Whats your firstname', 'answer' : ''})
request.session['question'].append({'question' : 'Whats your firstname', 'answer' : ''})
I need to display these questions to the user along with an input box for each question. When the user submits the form, I need to dump the input into the answers part of the session variable. Could someone show me how to do this? I'm a little lost as this isn't really based on Django forms or models as such.
Thanks
You could use forms that aren't associated with models, like this:
class QuestionForm(forms.Form):
answer = forms.CharField()
def questions(request):
if request.method == 'POST':
form = QuestionForm(request.POST)
if form.is_valid():
# Process the data in form.cleaned_data
return HttpResponseRedirect('/done/')
else:
form = QuestionForm() # An unbound form
return render_to_response('questions.html', {'form': form,})
More documentation here.
I've made a nice form, and a big complicated 'add' function for handling it. It starts like this...
def add(req):
if req.method == 'POST':
form = ArticleForm(req.POST)
if form.is_valid():
article = form.save(commit=False)
article.author = req.user
# more processing ...
Now I don't really want to duplicate all that functionality in the edit() method, so I figured edit could use the exact same template, and maybe just add an id field to the form so the add function knew what it was editing. But there's a couple problems with this
Where would I set article.id in the add func? It would have to be after form.save because that's where the article gets created, but it would never even reach that, because the form is invalid due to unique constraints (unless the user edited everything). I can just remove the is_valid check, but then form.save fails instead.
If the form actually is invalid, the field I dynamically added in the edit function isn't preserved.
So how do I deal with this?
If you are extending your form from a ModelForm, use the instance keyword argument. Here we pass either an existing instance or a new one, depending on whether we're editing or adding an existing article. In both cases the author field is set on the instance, so commit=False is not required. Note also that I'm assuming only the author may edit their own articles, hence the HttpResponseForbidden response.
from django.http import HttpResponseForbidden
from django.shortcuts import get_object_or_404, redirect, render, reverse
#login_required
def edit(request, id=None, template_name='article_edit_template.html'):
if id:
article = get_object_or_404(Article, pk=id)
if article.author != request.user:
return HttpResponseForbidden()
else:
article = Article(author=request.user)
form = ArticleForm(request.POST or None, instance=article)
if request.POST and form.is_valid():
form.save()
# Save was successful, so redirect to another page
redirect_url = reverse(article_save_success)
return redirect(redirect_url)
return render(request, template_name, {
'form': form
})
And in your urls.py:
(r'^article/new/$', views.edit, {}, 'article_new'),
(r'^article/edit/(?P<id>\d+)/$', views.edit, {}, 'article_edit'),
The same edit view is used for both adds and edits, but only the edit url pattern passes an id to the view. To make this work well with your form you'll need to omit the author field from the form:
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
exclude = ('author',)
You can have hidden ID field in form and for edit form it will be passed with the form for add form you can set it in req.POST e.g.
formData = req.POST.copy()
formData['id'] = getNewID()
and pass that formData to form