i can't figure out how to save my form data creating a new row, when saving it just rewrites the data using the same 'id' and trhows me an error when there are multiple rows, this is my code:
models.py:
class Submitter(models.Model):
submitter=models.ForeignKey(User)
def __unicode__(self):
return self.submitter.username
class Store(models.Model):
creator=models.ForeignKey(Submitter)
name = models.CharField(_('name'),blank=True,max_length=30)
st = models.CharField(_('Street'),blank=True,max_length=30)
sub_date = models.DateField(_('Visit Date'),)
def __str__(self):
return u'%s-%s-%s'%(self.creator,self.name,self.sub_date)
views.py:
def StoreSave(request):
if request.method == 'POST':
form = StoreForm(request.POST)
if form.is_valid():
submitter, dummy= Creator.objects.get_or_create(creator=request.user)
store, created = Store.objects.get_or_create(
submitter=submitter
)
store.name = form.cleaned_data['name']
store.st = form.cleaned_data['st']
store.visit_date = form.cleaned_data['visit_date']
store.save()
return HttpResponseRedirect('/user/%s/' % request.user.username)
else:
form = StoreForm()
variables = RequestContext(request, {
'form': form
})
return render_to_response('store/create_store.html', variables)
If you want to create a new row, create it. :-) Like
store = Store(submitter=submitter,
name=form.cleaned_data['name'],
st=form.cleaned_data['st'],
store.visit_date=form.cleaned_data['visit_date'])
store.save()
Now you use get_or_create method which tries to find a row with given parameters, so that's why you updating it. And this method throws an error when there are multiple rows, yes, it's its normal behavior.
By the way it's better to place this saving code in form's method (save for example).
P. S. Just noticed you don't have visit_date field in your model, I think you meant sub_date.
Instead of using get_or_create you can simply use create
Store.objects.create(
submitter=submitter,
name=form.cleaned_data['name'],
st=form.cleaned_data['st'],
visit_date=form.cleaned_data['visit_date']
)
More information about the differences can be found Django Model() vs Model.objects.create()
Related
I have been going back and forward with ideas doing this and none of them has worked so I'm gonna ask for ideas. I have model called "List" that users can create for example "Sleep" Then they have input where they select one of their lists and can add value for example 8. Good sleep! After that user sees a graph based on the data that he/(she has entered. All that is already working but I cannot seem to find a way to get data organized.
I'm trying to do it so list "sleep" calls all it's values to the graph and then other list lets say "money" shows all its money values. I am using chart.js to draw the graphs and I dont care if they are all at the same time on the screen or if one by one but for somereason I cannot call these querysets properly. Only way it works is if I use .all() and that wont work since I have multiple users.
Models:
class List(models.Model):
name = models.CharField(max_length=100, default="")
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='lists')
def __str__(self):
return self.name
class Meta:
unique_together = ['name', 'user']
class DataItem(models.Model):
data = models.IntegerField(default=0)
list = models.ForeignKey(List, on_delete=models.CASCADE, related_name='data_items')r code here
Views
#login_required
def app(request):
form = list_form
form2 = data_form
user = request.user.pk
user_lists = List.objects.filter(user=user)
context = {'user_lists': user_lists, 'form': form, 'form2': form2}
return render(request, 'MyApp/app.html', context)
#require_POST
def addItem(request):
form = list_form(request.POST)
if form.is_valid():
user = request.user
new_list = List(name=request.POST['text'], user=user)
new_list.save()
return redirect('/app/')
#require_POST
def addData(request):
form = data_form(request.POST)
if form.is_valid():
new_data = DataItem(data=request.POST['data'], list=List.objects.get(id=request.POST['selection']))
new_data.save()
return redirect('/app/')
"addItem" adds an list (money and sleep)
"addData" adds an value to selected list
I have no glue where to start making what I descibed.. Should I filter the data sets in main view? make own view for it and call them with buttons?
Please be specific if you have some idea how to make that kind of function since i'm pretty new to django. Thanks :)
I take it you want to get all lists that a User has created, and then create graphs for them. First of all, I recommend adding a DateTimeField() to the DataItem model. For example: created = models.DateTimeField(auto_now_add=True).
Then, in a view, you can get all of the data for a users list like this:
#login_required
def app(request):
form = list_form
form2 = data_form
user = request.user.pk
user_lists = List.objects.filter(user=user)
list_data = {}
for list in user_lists:
list_data[list.name] = DataItem.objects.filter(list=list)
context = {'user_lists': user_lists, 'form': form, 'form2': form2, 'list_data': list_data}
return render(request, 'MyApp/app.html', context)
This will save all of the data you need in list data. If you took my advice at the beginning of the response, you could also sort it by created to get chronological. In the template, you can loop through this list. The key is the name of the list, and the value will be a queryset of the data entries.
Say I have a form that looks like this:
forms.py
class CreateASomethingForm(ModelForm):
class Meta:
model = Something
fields = ['field2', 'field3', 'field4']
I want the form to have these three fields. However my Somethingclass also has field1. My question is - how do I add data to field1, if I am not using the ModelForm to collect the data. I tried doing something like this, but it isn't working and I am unsure on the proper way to solve this:
views.py
def create_something_view(request):
if (request.method == 'POST'):
# Create an object of the form based on POST data
obj = CreateASomething(request.POST)
# ** Add data into the blank field1 ** (Throwing an error)
obj['field1'] = request.user
# ... validate, save, then redirect
The error I receive is:
TypeError: 'CreateAClassForm' object does not support item assignment
In Django, what is the proper way to assign data to a ModelForm object before saving?
form = CreateASomething(request.POST)
if form.is_valid():
obj = form.save(commit=False)
obj.field1 = request.user
obj.save()
Sometimes, the field might be required which means you can't make it past form.is_valid(). In that case, you can pass a dict object containing all fields to the form.
if request.method == 'POST':
data = {
'fields1': request.user,
'fields2': additional_data,
}
form = CreateASomethingForm(data)
if form.is_valid():
form.commit(save)
There are two ways given by Django official
LINK : https://docs.djangoproject.com/en/3.0/topics/forms/modelforms/
Method 1]
author = Author(title='Mr')
form = PartialAuthorForm(request.POST, instance=author)
form.save()
Method 2]
form = PartialAuthorForm(request.POST)
author = form.save(commit=False)
author.title = 'Mr'
author.save()
Here is a more suitable way to add data especially used during testing:
First convert an existing entry into a dictionary with the model_to_dict function
from django.forms.models import model_to_dict
...
valid_data = model_to_dict(entry)
Then add the new data into this dictionary
valid_data['finish_time'] = '18:44'
This works better than setting the value in the form
update_form.finish_time = '18:44'
Create the form with the valid data and the instance
update_form = UserEntryForm(valid_data, instance=entry)
Do any assertions you require:
self.assertTrue(update_form.is_valid())
entry = update_form.save()
self.assertEqual(
entry.status,
1
)
I have a form called Vehicles and i'm trying to assign a unique id to each one, each time a user completes one.
class Vehicles(models.Model):
id = models.DecimalField(primary_key=True, unique=True)
Trying to avoid race conditions(when two forms are being submitted in the same time) after the initial value that I assign to the id field according to the last vehicle-db-record, before saving the form I query again the db for the id of the last record. More or less I do it this way:
def vehicle(request):
vehicles= Vehicles.objects.all().order_by("-id")[0]
id = vehicles.id+1
if request.method == 'POST':
form = VehiclesForm(data=request.POST)
if form.is_valid():
vehicles= Vehicles.objects.all().order_by("-id")[0]
id = vehicles.id+1
temp = form.save(new_veh_id=id)
return render_to_response('success.html', locals(), context_instance= RequestContext(request))
else:
form = VehiclesForm(initial={'id': id})
return render_to_response('insertVehicle.html', locals(), context_instance= RequestContext(request))
and in forms I override the save method:
def save(self, *args, **kwargs):
commit = kwargs.pop('commit', True)
new_veh_id = kwargs.pop('new_veh_id', None)
instance = super(VehiclesForm, self).save(*args, commit = False, **kwargs)
if id is not None: instance.id = new_veh_id
if commit:
instance.save()
return instance
but is_valid returns false with form error:vehicles with this id already exists.
I use exactly this practice with another model and form (identical fields to these) and it works like a charm, forms pass validation despite the same id and it changes it in the last submitted form. Can anyone help me on this or suggest a better solution to achieve this functionality? What I do is maybe somehow 'custom'.
EDIT: I also tried this, but is_valid fails again
def clean(self):
cleaned_data = super(VehiclesForm, self).clean()
id = unicode(self.cleaned_data.get('id'))
vehicles= Vehicles.objects.all().order_by("-id")[0]
id = vehicles.id+1
cleaned_data[id] = id
return cleaned_data
I think you need select_for_update which locks the rows until the end
of transaction. Mind you that although it is designated to work with many
rows, you can still lock just one row, by making sure your filter query
will return only one object.
I am using django and as I am pretty new I have some questions.
I have one model called Signatures and a ModelForm called SignatureForm in my models.py file:
class Signature(models.Model):
sig = models.ForeignKey(Device)
STATE = models.CharField(max_length=3, choices=STATE_CHOICES)
interval = models.DecimalField(max_digits=3, decimal_places=2)
verticies = models.CharField(max_length=150)
class SignatureForm(ModelForm):
class Meta:
model = Signature
widgets = {
'verticies': HiddenInput,
}
To use it, I wrote the following function in views.py:
def SigEditor(request):
# If the form has been sent:
if request.method == 'POST':
form = SignatureForm(request.POST)
# If it is valid
if form.is_valid():
# Create a new Signature object.
form.save()
return render_to_response('eQL/sig/form_sent.html')
else:
return render_to_response('eQL/sig/try_again.html')
else:
form = SignatureForm()
return render_to_response('eQL/sig/showImage.html', {'form' : form})
However, I don't want to save all the new signatures. I mean, if the user introduces a new signature of the device A and state B, I would like to check if I have some signature like that in my database, delete it and then save the new one so that I have only one signature saved for each device and state.
I have tried something like this before saving it but of course is not working:
q = Signature.objects.filter(sig = s, STATE = st)
if q.count != 0:
q.delete()
form.save()
can anyone help?? thanks!!
If you really do want to delete, why not?
Signature.objects.filter(sig=s, STATE=st).delete()
If you only ever want one combination of those items, you could use get_or_create, and pass in the instance to your ModelForm.
instance, created = Signature.objects.get_or_create(sig=s, STATE=st)
form = SignatureForm(request.POST, instance=signature)
# edit instance.
Or put it in your form save logic:
class SignatureForm(ModelForm):
def save(self, *args, **kwargs):
data = self.cleaned_data
instance, created = Signature.objects.get_or_create(sig=data['sig'], STATE=data['state'])
self.instance = instance
super(SignatureForm, self).save(*args, **kwargs)
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