So I have a function in the Django admin that allows me to create a duplicate MyModel in the database:
def save_model(self, request, obj, form, change):
if '_saveasnew' in request.POST:
old_obj_id = resolve(request.path).args[0]
old_obj = MyModel.objects.get(id=old_obj_id)
obj.other_id = old_obj.other_id
obj.status = old_obj.status
obj.project_id = old_obj.project_id
obj.test_url = old_obj.test_url
obj.save()
super(MyModelAdmin, self).save_model(request, obj, form, change)
This creation works fine, but I have another system interacting with this database that is seeing insert failures every time this function has been called. For example, if I create 2 duplicate entries in the Django admin this way, then the other system will see two errors like
IntegrityError duplicate key value violates unique constraint "my_model_pkey" DETAIL: Key (id)=(1234) already exists.
I'm using Django 1.11.15 & PostgreSQL 9.5.15.
My best guess is that somewhere your code is telling your database to create a new object row and explicitly set the ID of that row to be X. When really the code should be telling your database to create a new object row and implicitly set the ID of that row to be whatever the next available integer is.
Your code is confusing because you're doing it in a very complicated way. Why does this function take in both an object and a request? And then it finds the old object from the request? Where was the new object created?
A simpler way would be to first check if you want to save as new. If not, have a function that updates an object and give it the existing object. If it is a Save As New Request, create a new object row that has similar the values as the existing object (except ID). And then update that new object with the changes. Or however your logic should work. In any case, there is a much more straightforward way of accomplishing the steps that you want to happen if you think about how the steps are ordered.
Related
How to use PUT method for creating an object on particular id if no object is available
on that id in Django Rest Framework?
You can try update_or_create()
e.g:
class YourAPIView(APIView):
def put(self, request, **kwargs):
serializer = YourSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
obj, created = YourModel.objects.update_or_create(
id=kwargs['id'],
defaults=serializer.validated_data)
return Response()
A RESTFUL API should error out for a PUT request on an object that doesn't exist. The idea being that if it had existed at one point to create the id, it has since been deleted. It makes more sense to keep it deleted than to re-create it.
This is especially true if the id is auto-generated. And even more so if it's an auto-incrementing integer id like the default id of Django models. If you were to support this functionality in that case, a user would create an instance of data with an id that the table hasn't incremented over yet potentially leading to errors like this.
I have a user with the name field "jule".
If i try to update it with exactly the same name like this :
obj, created = Person.objects.update_or_create(first_name='jule', defaults={'first_name': 'jule'})
Will this make an action in my database? Or does the django ORM detect that there is no change to make (and not do any action in my database then) ?
I need this to optimize my code wihtin a loop :)
Thank for your help !
from the docs:
The update_or_create method tries to fetch an object from database based on the given kwargs. If a match is found, it updates the fields passed in the defaults dictionary.
This is meant as a shortcut to boilerplatish code. For example:
defaults = {'first_name': 'Bob'}
try:
obj = Person.objects.get(first_name='John', last_name='Lennon')
for key, value in defaults.items():
setattr(obj, key, value)
obj.save()
except Person.DoesNotExist:
new_values = {'first_name': 'John', 'last_name': 'Lennon'}
new_values.update(defaults)
obj = Person(**new_values)
obj.save()
the sample code describes what exactly the update_or_create does.
No, you've just used first_name='jule' as a filter to get the db entry. That only will check and will not do any update since you are not using defaults dict in the call.
More info https://docs.djangoproject.com/en/3.0/ref/models/querysets/#update-or-create
Some more info about the case when it is created.
https://github.com/django/django/blob/d6505273cd889886caca57884fa79941b18c2ea6/django/db/models/query.py#L576
It doesn't do any update if the object didn't exist. Just creates it.
You don't have to put defaults={'first_name': 'jule'} into the method since it is done by Django "under the hood".
defaults dict is a dict with extra changes, using defaults you can "rename" your user I believe by passing a new name, but don't need to pass the same params there :)
I'm using the Django admin and trying to make some changes to a related object that is mapped as an InlineModelAdmin object. I'm trying to do this using the save_related(self, request, form, formsets, change) method that Django provides. When I try to save something, I get an error:
AttributeError: 'AlumniResponseFormFormSet' object has no attribute 'new_objects'
Other Info:
1) I have two InlineModelAdmins
2) I'm not saving the AlumniResponseInline when this error occurs. I'm saving another InlineModelAdmin associated with the same parent model
3) Until I added the save_related() method, I wasn't having problems saving either InlineModelAdmin
4) This error is happening after all my code is executed in save_related(), so I don't have control over catching that exception
From the documentation on save_related():
The save_related method is given the HttpRequest, the parent ModelForm instance, the list of inline formsets and a boolean value based on whether the parent is being added or changed. Here you can do any pre- or post-save operations for objects related to the parent. Note that at this point the parent object and its form have already been saved.
I use save_formset instead of save_related and I was having the same problem until I realized that I missed two important lines inside the method:
instances = formset.save(commit=False)
at the beginning, and then, after loop instances to do something with each instance:
instance.save() #commit instance changes
formset.save_m2m() #commit the whole formset changes
at the end.
If you don't call the save_m2m() method before return, the formset object won't have the 'new_objects' attribute, needed in the construct_change_message(self, request, form, formsets) method in contrib/admin/options.py
So, this should be done for every inline you have in the main model, no matter whether you want to make something with it or not.
I have a Django model called StaffSettings which contains various configuration options for users in my Django app. Each User has at most one entry in the StaffSettings table.
Assume that one setting is default_year_level, and I have code for my user objects like:
def set_default_year_level(u, year_level):
obj, _created = StaffSettings.objects.get_or_create(user=u)
obj.default_year_level = year_level
obj.save()
I would prefer the body of the function to fit onto one line because it seems like a common use case, but if I defined it as
def set_default_year_level(u, year_level):
StaffSettings.objects.filter(user=u).update(default_year_level=year_level)
which works fine if the user in question already has a row in the StaffSettings table, but it won't create the relevant row if it doesn't exist.
What is the idiomatic/best way to code this? (e.g. Is there some sort of filter_or_create function? Or do other people write decorators/helper functions to handle this idiom?)
I don't see any problem with your first function, I would have written the same for this usecase.
However if you need the same feature on a lot of fields on your model and you don't want to repeat yourself you can pass the field as parameter :
def set_default_value(u, field, value):
obj, _created = StaffSettings.objects.get_or_create(user=u)
setattr(obj, field, value)
obj.save()
And I will stay away from the update() function anyway as this function is meant to update multiple objects at once and does not trigger the save() method nor signals on your models (see https://docs.djangoproject.com/en/dev/topics/db/queries/#updating-multiple-objects-at-once)
Since Django 1.7 you can use update_or_create():
def set_default_year_level(u, year_level):
obj, _created = StaffSettings.objects.update_or_create(
user=u,
default_year_level=year_level
)
Or, for a more generic case as explained in the previous answer:
def set_default_values(u, **kwargs):
obj, _created = StaffSettings.objects.update_or_create(user=u, defaults=kwargs)
which also achieves your additional requirement
I would prefer the body of the function to fit onto one line
If a have a form, with the data from a user, let's say a CV, and i save the data from the form into a database, but i don't want that a CV from the same user to be stored in the database more than once(when edited form instance)
I want it to be overwritten every time it is saved by one same user.
How can i do it?
thanks a lot
Django's save() should handle this for you automatically.
To give an example, you'll usually submit a form in a way something like this:
...
form = UserCVForm(request.POST, instance=user_cv)
if form.is_valid():
form.save()
...
'instance=user_cv' tells django that you want to update an existing entry - specifically 'user_cv'. Without 'instance=user_cv', Django will insert a new entry into the database.
So in short, see if a user_cv exists already with something like user_cv = UserCV.objects.get(user=user_id). If a user_cv exists, be sure to whack an instance=user_cv in when populating the form.