I'm misunderstanding something! If my model is not saved, it does not have an id associated with it. So if I have something like this:
views.py (inserting or editing existing info uses the same modelform)
def insert_or_modify(request, id=None):
if id is not None:
book = BookModel.objects.get(pk=id)
else:
book = BookModel()
if request.method == 'POST':
form = BookInfoForm(request.POST, request.FILES, instance=book)
if form.is_valid():
form.save()
....
return render_to_response(...)
I also have an image and use upload_to for the imagefield. There are two problems: id is None and I'm not sure how to manipulate/save the instance=book so that I would actually get an id. The second problem is the location I save my data to is way off. Even though the template tag book.img.url has the desired location of the book at http:127.0.0.1:8000/folder1/media/id/, the actual location is somewhere else:
Where I want to save my image to:
/project/folder1/media/id/
where id is the book id.
What I actually get:
/project/id/
(But 'id' becomes 'None' since it doesn't exist!)
My previous code worked. It would save to the correct location, but with this current code, it doesn't work. So the saving issue doesn't seem like it's due to settings.py since it worked previously.
EDIT: removed non-code from code formatting area
EDIT: I found out why I wasn't saving to the correct location. As it turned out, I forgot to uncomment something when I last modified settings.py. Saving to the location works now! Sorry guys!
EDIT: I think the id=None problem is caused by form.save(). If I avoid doing that and just save to the model directly, I don't have this problem.
Id assigns only on saving objects when you use autoincrement id field (default).
You can save item before handling image, and then save image.
May be you can not worry about image name - becouse django file storages dont have troubles with same image names. So if you just save file "image.png", and then save another file with name "image.png" - then it will be saved as "image_1.png"
def add_or_create(request, item_id=None):
item = get_object_or_404(BookModel, id=item_id) if item_id else None
form = BookInfoForm(request.POST or None, request.FILES or None, instance=book) # assume it is ModelForm
if form.is_valid():
book = form.save()
For the first part:
def insert_or_modify(request, id=None):
if id:
book = BookModel.objects.get(pk=id)
if request.method == 'POST':
form = BookInfoForm(request.POST, request.FILES, instance=book)
if form.is_valid():
save_book = form.save()
# use save_book as your instance of BookModel
....
else:
if request.method == 'POST':
form = BookInfoForm(request.POST, request.FILES)
if form.is_valid():
save_book = form.save()
# use save_book as your instance of BookModel
....
save_book = form.save() allows you to then use save_book as your saved instance of BookModel, and save_book.id is its id.
def create_id(instance,some_id=None):
if some_id is None:
obj=Post.objects.first()
new_id=obj.id
new_id+=1
return new_id
else:
return some_id
def pre_save_post_receiver(sender, instance, *args, **kwargs):
if not instance.id:
instance.id = create_id(instance)
pre_save.connect(pre_save_post_receiver, sender=Post)
Related
I am trying to use same view for creating form and updating any object.
My code is as below, I tried in many ways nothing is working, since I am excluding the shof from form and adding it after form.is_valid() it makes lot of confusion. If I update it creates new object. I have two urls one without ql (create new) and one with ql (update existing), I have a class vdview which provides v.shof which needs to applied in the f.shop in form. please help fix this,
#csrf_protect
#login_required
def addmenu(request, qs, ql=None):
v = vdview(request, qs)
ctgobj = get_object_or_404(v.shopcategs, pk=ql) if ql else None # ctgobj = ShopCtg(shop=v.shof)
if ql:
form = ShopCtgForm(instance=ctgobj) # Tried ShopCtgForm(instance=ctgobj, data=request.POST)
else:
form = ShopCtgForm(data= request.POST)
if request.method == 'POST':
if form.is_valid():
f=form.save(commit=False)
f.shop = v.shof
f.save()
#form.save_m2m()
return redirect('vendor-shop', qs) #thing='%s added' %f.name)
else:
pass
#else:
# form = ShopCtgForm()
return render(request,'vendorshop.html', {'shop':v.shof, 'shopcategs':v.shopcategs, 'form': form,
'heading':'Create New Category', 'createcateg': 'createcateg', 'pkaddmenupk':'y' } )
Use try blocks to handle both scenarios. The simplified example below will look for a given model instance pk and if it doesn't find it, will assume you want to create it. try will prevent django from throwing an error if the model instance doesn't exist. Rather, it will just return the empty model form.
It does this first to render the correct form in the template (the first try block) then again in the second try block after request.method == 'POST': to submit new data or update existing data.
Views.py
from .models import Books
from .forms import BookForm
def create_and_update_book_view(request, pk):
books = Books.objects.get(id=pk)
try: # get pre-populated form with model instance data (for update)
form = BookForm(instance=books.id)
except: # If it doesn't exist, show an empty form (for create)
form = BookForm(request.POST or None)
if request.method == 'POST':
try: # Do the same as above
form = BookForm(instance=books.id)
except: # Same as above
form = BookForm(request.POST or None)
if form.is_valid():
form.save()
return render(request, "create_and_update_book_page.html", {'form':form})
I'm creating a form in one page, then in another page I'm trying to pull out the form (populated with the data saved in it already) and would like to make changes to it so that when I save it it overwrites the instance instead of creating another one.
def edit(request):
a = request.session.get('a', None)
if a is None:
raise Http404('a was not found')
if request.method == 'POST':
form = Name_Form(request.POST, instance=a)
if form.is_valid():
j = form.save( commit=False )
j.save()
else:
form = Name_Form( instance = a )
This is the code I have for the "editting form" view.. When I open this page the form is successfully prepopulated with all the data. However, when I make changes and save, it does not overwrite the existing instance, instead it creates a new one.
Any ideas?
Have a look here: https://docs.djangoproject.com/en/dev/ref/models/instances/#how-django-knows-to-update-vs-insert
I think this may help you.
Update:
What about trying a more "explicit" way.
Assume, id_of_Name stores only the id or pk of your model which you want to edit (I assume the model is called "Name"). Then just retrieve the id/pk from session to query your db for the model instance. Also try to directly call the save method on the form.
def edit(request):
id_of_Name = request.session.get('a', None)
if id_of_Name is None:
raise Http404('id_of_Name was not found')
instance = Name.objects.get(pk=id_of_Name)
if request.method == 'POST':
form = Name_Form(request.POST, instance=instance)
if form.is_valid():
form.save()
else:
form = Name_Form( instance = instance )
I am using django forms to add a new objects to the db. The code I currently have is:
if request.method == 'POST':
form = MyForm(data=request.POST)
if form.is_valid():
obj = form.save()
else:
form = MyForm()
return render_to_response('reflections/add_reflection.html', {'form':form},context_instance=RequestContext(request))
The code above currently adds a new object each time the form is submitted. What I want to happen is that the object is edited the next time the save button is pressed rather than adding a new record.
How would I do this?
Use
instance_id = None
if request.method == 'POST':
try:
instance = MyType.objects.get(id=request.POST.get('instance_id'))
except MyType.DoesNotExist:
instance = None
form = MyForm(data=request.POST, instance=instance)
if form.is_valid():
obj = form.save()
instance_id = obj.id
else:
form = MyForm(instance=None)
return render_to_response('reflections/add_reflection.html', {'form':form, 'instance_id': instance_id or ''},context_instance=RequestContext(request))
Once the object is saved, pass it's id in context to page
and add it to a hidden input field inside the form as name='instance_id'.
Happy Coding.
You either need to add a separate view for editing an existing object, or - better - to add the functionality to this view. To do the latter, you could pass in an instance of the object you want to edit with your modelform to else part of your clause:
else:
if existing_obj:
form = MyForm(instance=existing_obj) #this is editing your 'existing_obj'
else:
form = MyForm() # this is creating a brand new, empty form
You'll also need to update the POST-handling bit of code too. See the example here
how do I set the value of a field element after a form has been submitted but has failed validation? e.g.
if form.is_valid():
form.save()
else:
form.data['my_field'] = 'some different data'
I don't really want to put it in the view though and would rather have it as part of the form class.
Thanks
The documentation says:
If you have a bound Form instance and want to change the data somehow, or if you want to bind an unbound Form instance to some data, create another Form instance. There is no way to change data in a Form instance. Once a Form instance has been created, you should consider its data immutable, whether it has data or not.
I cannot really believe that your code works. But ok. Based on the documentation I would do it this way:
if request.method == 'POST':
data = request.POST.copy()
form = MyForm(data)
if form.is_valid():
form.save()
else:
data['myField'] = 'some different data'
form = MyForm(initial=data)
I ended up doing
if request.method == 'POST':
new_data = request.POST.copy()
form = MyForm(data=new_data)
if form.is_valid():
form.save()
else:
new_data['myField'] = 'some different data'
Hope this helps someone
You can put it in the form class like this:
class MyForm(forms.Form):
MY_VALUE = 'SOMETHING'
myfield = forms.CharField(
initial=MY_VALUE,
widget=forms.TextInput(attrs={'disabled': 'disabled'})
def __init__(self, *args, **kwargs):
# If the form has been submitted, populate the disabled field
if 'data' in kwargs:
data = kwargs['data'].copy()
self.prefix = kwargs.get('prefix')
data[self.add_prefix('myfield')] = MY_VALUE
kwargs['data'] = data
super(MyForm, self).__init__(*args, **kwargs)
The way it works, is it tests to see if any data has been passed in to the form constructor. If it has, it copies it (the uncopied data is immutable) and then puts the initial value in before continuing to instantiate the form.
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