django update form has no attribute save - django

I am trying to create a form which will allow users to update their name, I am using a forms.form instead of ModelForm because it gives you more control over the styling of the form because you can use widgets. when I go to save the form it says that updateNameForm has no attribute save
view
def UpdateName(request,user_id):
if request.method == "POST":
form = UpdateNameForm(request.POST,initial=initial)
if form.is_valid():
name = form.cleaned_data['name']
form.save()
else:
form = UpdateNameForm(initial=initial)
forms.py
class UpdateNameForm(forms.Form):
name = forms.CharField(
required=True,
label="*Status",
widget=forms.widgets.Select(attrs={'class' : 'span6 small-margin-top small-margin-bottom'})
)

A standard form class indeed has no save attribute - it doesn't know anything about any models to save to. You need to use a ModelForm.
Note also you need to pass the instance argument to the form to make it update an existing instance, rather than create a new one. You probably don't need initial at all.

def UpdateName(request,user_id):
user = get_user(user_id)
if request.method == "POST":
form = UpdateNameForm(request.POST,initial=initial)
if form.is_valid():
name = form.cleaned_data['name']
user.name = name
user.save()
else:
form = UpdateNameForm(initial=initial)

Related

Adding a value to a database field that is in your model but is not in your form

I have a model with a property called authID that looks like this:
authID = models.CharField(max_length=400)
In my view, all of the fields that are in my form get put into the database as follows:
if request.method == 'POST':
form = PersonalDetailsModelForm(request.POST)
if form.is_valid():
form.save()
What I want to know how to do is that when they submit the form, I want to also put the value of authID into my database. However, authID is not a field in my form. I don't want to make it a hidden field either for security reasons.
How do you add a value to a database field that is in your model, but is not in your form?
You can override the save() method on PersonalDetailsModelForm to set this value before saving the instance:
def save(self, commit=True):
self.instance.authID = 'some_value' # Set the auth ID
return super().save(commit=commit)
If the authID is coming from your view, then you can pass that as a keyword argument to your overridden save() method.
Call the save() method of Django Form with commit=False
#views.py
if request.method == 'POST':
form = PersonalDetailsModelForm(request.POST)
if form.is_valid():
model_instance = form.save(commit=False) # 'commit=False' plays key role here
model_instance.authID = 'your auth id'
model_instance.save()
...
Reference
Django model form save() method

Create, get and edit user information in same form and template

I'm trying to retrieve data from user. The form where i want to show the user information is also the same that i use to update this information.
Update3
After some updates I make this work and this is my code. If somenone have a better way to do this can share it :)
models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class informacionFacturacion(models.Model):
usuario = models.ForeignKey(User, on_delete=models.CASCADE)
apellidos = models.CharField(max_length=100)
nombres = models.CharField(max_length=100)
[More fields...]
def __str__(self):
self.apellidos
forms.py
from .models import informacionFacturacion
#Create your forms here.
class informacionFacturacionForm(ModelForm):
class Meta:
model = informacionFacturacion
fields = [
"usuario",
"apellidos",
"nombres",
[More fields...]
]
views.py
#login_required
def datosPersonales(request):
#Filter query by user ID
query = informacionFacturacion.objects.filter(usuario=request.user)
form = informacionFacturacionForm()
#If query has content, edit record, else, create a new record
if query:
if request.method == "POST":
form = informacionFacturacionForm(request.POST or None, instance=query[0])
if form.is_valid():
edit_content = form.save()
edit_content.save()
else:
if request.method == "POST":
form = informacionFacturacionForm(request.POST)
if form.is_valid():
create_content = form.save(commit=False)
create_content.save()
return HttpResponseRedirect(reverse('datosPersonales'))
context = {
"titulo": "Datos personales | Co.",
"body_class": "class= sidebar_main_open sidebar_main_swipe",
"form": form,
"infoFacturacion": query,
}
template = "micuenta/datosPersonales.html"
return render(request, template, context)
Thanks for the support.
At first glance, it seems that the informacionFacturacion table is not being populated. Have you checked that the instance.save() is reached? (in other words, that the form is valid)
Second, in the template you want to use the informacionFacturacion object as the form elements, and you are handling them separately. Do:
if request.POST:
form = informacionFacturacionForm(request.POST)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
else:
# handle here the form error's, maybe report it in the template
else:
query = informacionFacturacion.objects.filter(usuario=request.user)
form = informacionFacturacionForm(instance=query[0])
and render the form parameter insead of infoFacturacion:
{{ form.as_p }}
finally, make sure that your template form id's matches the form element names, otherwise the form won't be filled.
UPDATE
Based on your edit, now the error is in this line:
form = informacionFacturacionForm(request.POST, instance=query_id)
query_id is an int, and it is expecting a model. Change the following line:
query_id = informacionFacturacion.objects.get(usuario=request.user).id
to
query = informacionFacturacion.objects.get(usuario=request.user)
and the faulty line to:
form = informacionFacturacionForm(request.POST, instance=query)
that should work for now, although code can be simplified a lot.
EDIT 2
Here is what I assume you want:
#login_required
def datosPersonales(request):
query = informacionFacturacion.objects.filter(usuario=request.user)
if request.method == "POST": # This will handle the template form's POST
form = informacionFacturacionForm(request.POST)
if form.is_valid():
asd = form.save(commit=False)
asd.save()
# Here you may want to redirect to somewhere else
# Im not sure here, I guess that you want to handle the GET method if
# there is no form in the request. Post your template form to see what
# is happening.
else:
form = informacionFacturacionForm(instance=query)
# you dont need to save it, it is already in DB
context = {
"titulo": "Datos personales | Co.",
"body_class": "class= sidebar_main_open sidebar_main_swipe",
# I think here is your main issue, you are handling a form object
# AND a infoFacturacion object. You need to use just the
# form object in the template and render it accordingly.
"form": form,
"infoFacturacion": query,
}
template = "micuenta/datosPersonales.html"
return render(request, template, context)
Well, I was with the same problem on my sytem, so I made this solution, maybe it works to you! =D
I'm changing the value of the submit button and using the same form:
<button type="submit" id="submitButton" name="button" value="">Save</button>
If is a new task, I change the value of the button with JQuery:
$('#submitButton').val('new');
And if is an edition, I change the value again:
$('#submitButton').val('edit');
On my views.py, I check if is an edit or a new save by the value of the button:
def index(request):
tasks = Task.object.filter()
context = {
'tasks': tasks
}
if request.method == 'POST':
form = NewTask(request.POST or None)
if request.POST['button'] == 'new':
if form.is_valid():
context['is_valid'] = True
form.save()
form = NewTask()
else:
context['is_valid'] = False
if request.POST['button'] == 'edit':
instance = Task.object.filter(pk=request.POST['id']).first()
form = NewTask(request.POST, instance=instance)
if form.is_valid():
context['is_valid'] = True
form.save()
else:
context['is_valid'] = False
else:
form = NewTask()
context['form'] = form
return render(request, 'index.html', context)

Django Foreign Key to a yet-to-be created Model

I'm wondering if there's way in Django to associate a model to another, yet-to-be created model with a foreign key. Both model would be created using the same ModelForm in the same HTML page.
e.g.
class Team(models.Model):
name = forms.CharField
...
class Player(models.Model):
name = forms.CharField()
key = forms.ForeignKey(Team)
...
Basically, I'm wondering if both these models can be put in the same <form>...</form> in one HTML page.
a foreign key is a reference to the primary key of the referenced model, so the target needs to exist. you need to save the first form, and then update the reference on the second one before saving. to get a model instance from a form without saving to the db, you can use
instance = form.save(commit=False)
you then need to save the instance yourself
instance.save()
and if you are using many-to-many fields, you need to look at save_m2m
You may want to check the documentation for inlineformset, it allows to edit the related objects of a model in the same view, also see formsets.
def manage_teams(request, team_id):
team = Player.objects.get(pk=team_id)
PlayerInlineFormSet = inlineformset_factory(Player, Team)
if request.method == "POST":
formset = PlayerInlineFormSet(request.POST, request.FILES, instance=team)
if formset.is_valid():
formset.save()
# Do something.
else:
formset = PlayerInlineFormSet(instance=team)
return render_to_response("manage_teams.html", {
"formset": formset,
})
Here goes another example:
from django.forms.models import inlineformset_factory
def new_team(request):
PlayerInlineFormSet = inlineformset_factory(Team, Player)
team= Team()
if request.method == 'POST':
form_team = TeamForm(request.POST, request.FILES, instance= team, prefix= 'team')
form_player = PlayerInlineFormSet(request.POST, request.FILES, instance= team, prefix= 'players')
if form_team.is_valid() and form_player.is_valid():
form_team.save()
form_player.save()
return HttpResponseRedirect('/teams/%s/' % team.slug)
else:
form_team = TeamForm( instance= team, prefix= 'team')
form_player = PlayerInlineFormSet(instance= team, prefix= 'players')
return render_to_response('Teams/new_team.html', {'form_team': form_team, 'form_player':form_player}, context_instance=RequestContext(request))

Pre-Setting django form data before displaying the form

I am displaying a django form and I want to prepare some field data before it
is passed to to be rendered. In the django docs, I see plenty of places
where form data is accessed, but none where form data is set before display.
Any thoughts or suggestions on how to do this?
Here's an example similar to the django docs.
-----------forms.py--------------
class BookForm(ModelForm):
author = forms.CharField(max_length=100)
title = forms.CharField(max_length=3,
widget=forms.Select(choices=TITLE_CHOICES))
birth_date = forms.DateField(required=False)
-----------views.py--------------
def author_view(request):
if request.method == 'POST':
# DO My processing...
form = BookForm()
# How can I edit, or preset my form fields here?
c = Context({
'form': form,
})
return prepCxt(request, 'book.html', c) # Wrapper for easy display
In your views you have:
def author_view(request):
if request.method == 'POST':
# DO My processing...
form = BookForm()
# How can I edit, or preset my form fields here?
c = Context({
'form': form,
})
return prepCxt(request, 'book.html', c) # Wrapper for easy display
You should move your form=BookForm() before the if:
def author_view(request):
form = BookForm()
if request.method == 'POST':
# DO My processing...
What happens is that the if "POST" section adds a value in form and then it could get overriden.
Secondly if you are trying to change something in the way it display you are probably best adding default/initial values:
Django set default form values:
BookForm(initial={ 'myfield': 'myval'})
if you are tryiong to change values that you want to save to the DB then you:
if form.is_valid():
myobject = form.save(commit=false)
myobject.myfield = mval
myobj.save()
form = BookForm(instance = myobjext)
Something else? Please be more specific.

Django Forms with get_or_create

I am using Django ModelForms to create a form. I have my form set up and it is working ok.
form = MyForm(data=request.POST)
if form.is_valid():
form.save()
What I now want though is for the form to check first to see if an identical record exists. If it does I want it to get the id of that object and if not I want it to insert it into the database and then give me the id of that object. Is this possible using something like:
form.get_or_create(data=request.POST)
I know I could do
form = MyForm(instance=object)
when creating the form but this would not work as I still want to have the case where there is no instance of an object
edit:
Say my model is
class Book(models.Model):
name = models.CharField(max_length=50)
author = models.CharField(max_length=50)
price = models.CharField(max_length=50)
I want a form which someone can fill in to store books. However if there is already a book in the db which has the same name, author and price I obviously don't want this record adding again so just want to find out its id and not add it.
I know there is a function in Django; get_or_create which does this but is there something similar for forms? or would I have to do something like
if form.is_valid():
f = form.save(commit=false)
id = get_or_create(name=f.name, author=f.author, price=f.price)
Thanks
I like this approach:
if request.method == 'POST':
form = MyForm(request.POST)
if form.is_valid():
book, created = Book.objects.get_or_create(**form.cleaned_data)
That way you get to take advantage of all the functionality of model forms (except .save()) and the get_or_create shortcut.
You just need two cases in the view before the postback has occurred, something like
if id:
form = MyForm(instance=obj)
else
form = MyForm()
then you can call form.save() in the postback and Django will take care of the rest.
What do you mean by "if an identical record exists"? If this is a simple ID check, then your view code would look something like this:
if request.method == 'POST':
form = MyForm(request.POST)
if form.is_valid():
form.save()
else:
if get_id:
obj = MyModel.objects.get(id=get_id)
form = MyForm(instance=obj)
else:
form = MyForm()
The concept here is the check occurs on the GET request, such that on the POST to save, Django will already have determined if this is a new or existing record.
If your check for an identical record is more complex, it might require shifting the logic around a bit.
I would do this -
if request.method == 'POST':
form = MyForm(request.POST)
if form.is_valid():
name = form.cleaned_data['name']
author = form.cleaned_data['author']
price = form.cleaned_data['prince']
if name and author and price:
book, created = Book.objects.get_or_create(name=name, \
author=author, price=price)
if created:
# fresh entry in db.
else:
# already there, maybe update?
book.save()
Based on the answers and comments, I had to create a different solution for my case, which included the use of unique_together on the base model. You may find this code useful as well, as I actually made it fairly generic.
I have custom code in the form.save() method that I want to utilize for creating a new object, so I don't want to simply not use the form.save() call. I do have to put my code check in the form.save() method, which I think is a reasonable place to put it.
I have a utility function to flatten iterables.
def flatten(l, a=list()):
"""
Flattens a list. Just do flatten(l).
Disregard the a since it is used in recursive calls.
"""
for i in l:
if isinstance(i, Iterable):
flatten_layout(i, a)
else:
a.append(i)
return a
In the ModelForm, I overwrite the validate_unique() method:
def validate_unique(self):
pass
This is about what my save method looks like:
def save(self, commit=True):
unique_fields = flatten(MyObject._meta.unique_together)
unique_cleaned_data = {k: v for k, v in self.cleaned_data.items() if k in unique_fields}
# check if the object exists in the database based on unique data
try:
my_object = MyObject.objects.get(**unique_cleaned_data)
except MyObject.DoesNotExist:
my_object = super(MyModelFormAjax, self).save(commit)
# -- insert extra code for saving a new object here ---
else:
for data, value in self.cleaned_data.items():
if data not in unique_fields:
# only update the field if it has data; otherwise, retain
# the old value; you may want to comment or remove this
# next line
if value:
setattr(my_object, data, value)
if commit:
my_object.save()
return my_object