Ceating two objects instead of one in Django - python

I want to create an object with transmitting some data from other model. And it works good, but instead of creation one object of model, I got two objects.
I create one object and try modify it, but it saves two objects, created and modified. I want to save only one object, which was modified. I am using the approach that was suggested to me: Django instance in model form
Views
topic = Topic.objects.get(id=pk)
room = Room.objects.create(topic=topic)
form = RoomForm(request.POST, instance=room)
if request.method == 'POST':
if form.is_valid():
room = form.save(commit=False)
room.host=request.user
room.save()
return redirect('home')

Don't create an object yourself; let the form do this. With your approach, you create one in the GET request, and one in the POST request:
def my_view(request, pk):
topic = Topic.objects.get(id=pk)
# no create
if request.method == 'POST':
form = RoomForm(request.POST)
if form.is_valid():
form.instance.topic_id = pk
form.instance.host = request.user
form.save()
return redirect('home')
else:
form = RoomForm()
# …

if you want to modify an object which created before just do this.
search from your model and update the field you want
Topic.objects.filter(id=pk).update(fields = something)
that it bro.
remember use if when you want to sure that the object you choose is the right one

Related

How to remove built-in errorlist of Django model forms?

I made a model form in Django that enables user to create an instance of that model. All is well save one thing. Built-in error lists of Django is annoying. I want those errors to show itself when user actually made that error. When I open my page it looks like this
Please help me how do I remove these errorlist temporarily so that when user left input fields empty. It pop ups itself?
I don't know if you need these codes to solve this problem, but here it is:
views.py file here
#login_required(login_url='/accounts/login/')
def article_create(request):
article_form = ArticleForm(request.POST or None, request.FILES)
if article_form.is_valid():
instance = article_form.save(commit=False)
instance.author = request.user
article_form.save()
return render(request, 'blog/article-create.html', {'form': article_form})
Thank you
You use the request.POST or None idiom - which I dislike, but never mind - for the data parameter, but not for the files parameter. Since you pass an (empty) dict for the files, Django takes your form as bound, and therefore tries to validate it.
If you insist on using this idiom, you need to do it for both:
article_form = ArticleForm(request.POST or None, request.FILES or None)
But really, it's much more Pythonic to be explicit:
if request.method == 'POST':
article_form = ArticleForm(request.POST, request.FILES)
if article_form.is_valid():
instance = article_form.save(commit=False)
instance.author = request.user
article_form.save()
return redirect('/')
else:
article_form = ArticleForm()
return render(request, 'blog/article-create.html', {'form': article_form})
Note, you always need to redirect after a successful POST, as I have done above.

Get saved object of a model form in Django?

I just want to access model details just after posting it with model form in Django. This guy also had asked the same thing but when i try the
accepted answer, it returns none type value.
Here is my code in 'views.py':
if request.method == 'POST':
if request.user.is_authenticated():
form = PostStoryForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
obj.author = request.user
new_post = obj.save()
print(new_post)
The Code above saves the form to the database successfully but 'new_post' variable is 'None'. For example when i tried to access 'new_post.title' which is a field in my model, it returns 'AttributeError' which says 'NoneType' object has no attribute 'title'
what am i doing wrong?
The models save() method does not return the instance
obj.author = request.user
obj.save() # this does not return anything. It just saves the instance it is called on.
Your instance already has the author set.
To access auto populated fields that haven't been set yet, you will have to fetch it from the database again after saving. This is the case, when the instance you called save() on did not already exist before.
new_obj = MyModel.objects.get(id=obj.id)

"save and add another" in Django (not admin): submit then pre-populate one field of form

I have a form, "results", where one of the fields, "subjectID", is many-to-many because there's more than one result for each subject. I want one of the submit buttons to let me save what I've entered, then redirect to the same form, now unbound except that the many-to-many "subjectID" field stays the same so I can enter more results for that subject.
Edit: I should have made it clear that I wanted the instance that I had selected in the subjectID field to stay the same. I posted the code below that actually seems to be working for me
from models.py
class ResultsForm(forms.Modelform):
class Meta:
model = models.Results
fields = ['subjectID', # this is the field want
# to populate the form with when I "save and add another"
'slideNum', # IntegerField
'resultType' ] # ForeignKey
from views.py
def addResults(request):
if request.method == 'POST'
form = ResultsForm(request.POST)
if form.is_valid():
form.save()
if 'Save_and_add_another' in request.POST:
subjectID = form.fields['subjectID']
prepop = {'subjectID' : subjectID}
form = ResultsForm(initial=prepop)
return render(request, 'slideAdmin/addResults.html', {'form': form})
elif 'Save_and_return' in request.POST:
return HttpResponseRedirect('/home/')
else:
form = ResultsForm()
return render(request, 'slideAdmin/addResults.html', {'form': form})
Right now when I click on "save and add another" from my addResults form, I get this error:
TypeError at /slidebox/addResults
'ModelMultipleChoiceField' object is not iterable
which happens when rendering {{ form.as_p }} in the template.
Edit: Changes I made to views.py
if 'Save_and_add_another' in request.POST:
subjectID = form.cleaned_data.get('subjectID')
form = ResultsForm(initial={'subjectID': subjectID})
return render(request, 'slideAdmin/addResults.html', {'form': form})
As far as I can tell, this change works. Thanks again
You should always use form.cleaned_data.get('subjectID') versus pulling the field directly from the post data. You need to pass in a list of the pk's for the M2M field.
Your view can also use a touch of cleanup:
from django.core.urlresolvers import reverse
def addResults(request):
form = ResultsForm(request.POST or None)
if request.method == 'POST' and form.is_valid():
form.save()
if 'Save_and_add_another' in request.POST:
subjectID = form.cleaned_data.get('subjectID', [])
if subjectID:
subjectID = subjectIDs.split(',')
form = ResultsForm(initial={'subjectID': subjectID})
elif 'Save_and_return' in request.POST:
return HttpResponseRedirect(reverse('home')) # don't hard code
return render(request, 'slideAdmin/addResults.html', {'form': form})
I'm not sure if you will be able to keep the form unbound when initialized.
Your form.fields is an ordered dict of django.forms.fields objects. You just want the ids, and not all the other info that comes across it.
Get the data straight from the POST dictionary.
subjectID = request.POST.get('subjectID', '')
If this is a true many to many model. You need to make sure the data is setup correctly for the initialization.
# We have to special-case M2Ms as a list of comma-separated PKs.
if isinstance(f, models.ManyToManyField):
initial[k] = initial[k].split(",")
Here is the initialization method from the django source code for Admin (or as I call it my super detailed and complicated Django cheat sheet, I am pedantic)
def get_changeform_initial_data(self, request):
"""
Get the initial form data.
Unless overridden, this populates from the GET params.
"""
initial = dict(request.GET.items())
for k in initial:
try:
f = self.model._meta.get_field(k)
except FieldDoesNotExist:
continue
# We have to special-case M2Ms as a list of comma-separated PKs.
if isinstance(f, models.ManyToManyField):
initial[k] = initial[k].split(",")
return initial
Some PEP8 nonsense as well
classes are camel case ex: class MyAwesomeClass(object):
everything else is lower with underscores. ex: awesome_id = awesome1245
Good Luck!!

Django: using ._get_deleted_forms and custom formset saving

I'm attempting to provide some custom formset handling. The user has the option of deleting a form and adding a form. Within the same function I'm trying to remove the associated object and add new objects. This is an example of the code I'm working on:
def addCategories(movie, category_formset):
if category_formset.deleted_forms:
for form in category_formset._get_deleted_forms:
obj_to_remove = form.save(commit=False)
movie.categories.remove(obj_to_remove)
for form in category_formset:
if form.cleaned_data.get('name') is not None:
obj = Category.objects.get_or_create_category(form.cleaned_data.get('name'))
movie.categories.add(obj)
movie.save()
The problem is that I don't think I'm using ._get_deleted_forms correctly. It claims that category_formset._get_deleted_forms is not iterable. And I'm not sure if this would work anyway -- since it might just add the deleted object right back to the model in the second for loop. Any ideas?
A workaround that doesn't seem very efficient:
def addCategories(recipe, category_formset):
if category_formset.deleted_forms:
for form in category_formset:
if form in category_formset.deleted_forms:
obj_to_remove = form.save(commit=False)
recipe.categories.remove(obj_to_remove)
else:
if form.cleaned_data.get('name') is not None:
obj = Category.objects.get_or_create_category(form.cleaned_data.get('name'))
recipe.categories.add(obj)
else:
for form in category_formset:
if form.cleaned_data.get('name') is not None:
obj = Category.objects.get_or_create_category(form.cleaned_data.get('name'))
recipe.categories.add(obj)
recipe.save()

How to use form values from an unbound form

I have a web report that uses a Django form (new forms) for fields that control the query used to generate the report (start date, end date, ...). The issue I'm having is that the page should work using the form's initial values (unbound), but I can't access the cleaned_data field unless I call is_valid(). But is_valid() always fails on unbound forms.
It seems like Django's forms were designed with the use case of editing data such that an unbound form isn't really useful for anything other than displaying HTML.
For example, if I have:
if request.method == 'GET':
form = MyForm()
else:
form = MyForm(request.method.POST)
if form.is_valid():
do_query(form.cleaned_data['start_date'], form.cleaned_data['end_date'])
is_valid() will fail if this is a GET (since it's unbound), and if I do:
if request.method == 'GET':
form = MyForm()
do_query(form.cleaned_data['start_date'], form.cleaned_data['end_date'])
else:
form = MyForm(request.method.POST)
if form.is_valid():
do_query(form.cleaned_data['start_date'], form.cleaned_data['end_date'])
the first call to do_query triggers exceptions on form.cleaned_data, which is not a valid field because is_valid() has not been called. It seems like I have to do something like:
if request.method == 'GET':
form = MyForm()
do_query(form['start_date'].field.initial, form['end_date'].field.initial)
else:
form = MyForm(request.method.POST)
if form.is_valid():
do_query(form.cleaned_data['start_date'], form.cleaned_data['end_date'])
that is, there isn't a common interface for retrieving the form's values between a bound form and an unbound one.
Does anyone see a cleaner way to do this?
If you add this method to your form class:
def get_cleaned_or_initial(self, fieldname):
if hasattr(self, 'cleaned_data'):
return self.cleaned_data.get(fieldname)
else:
return self[fieldname].field.initial
you could then re-write your code as:
if request.method == 'GET':
form = MyForm()
else:
form = MyForm(request.method.POST)
form.is_valid()
do_query(form.get_cleaned_or_initial('start_date'), form.get_cleaned_or_initial('end_date'))
Unbound means there is no data associated with form (either initial or provided later), so the validation may fail. As mentioned in other answers (and in your own conclusion), you have to provide initial values and check for both bound data and initial values.
The use case for forms is form processing and validation, so you must have some data to validate before you accessing cleaned_data.
You can pass a dictionary of initial values to your form:
if request.method == "GET":
# calculate my_start_date and my_end_date here...
form = MyForm( { 'start_date': my_start_date, 'end_date': my_end_date} )
...
See the official forms API documentation, where they demonstrate this.
edit: Based on answers from other users, maybe this is the cleanest solution:
if request.method == "GET":
form = MyForm()
form['start_date'] = form['start_date'].field.initial
form['end_date'] = form['end_date'].field.initial
else:
form = MyForm(request.method.POST)
if form.is_valid():
do_query(form.cleaned_data['start_date'], form.cleaned_data['end_date'])
I haven't tried this though; can someone confirm that this works? I think this is better than creating a new method, because this approach doesn't require other code (possibly not written by you) to know about your new 'magic' accessor.

Categories