Correct way to use get_or_create? - python

I'm trying to use get_or_create for some fields in my forms, but I'm getting a 500 error when I try to do so.
One of the lines looks like this:
customer.source = Source.objects.get_or_create(name="Website")
The error I get for the above code is:
Cannot assign "(<Source: Website>, False)": "Customer.source"
must be a "Source" instance.

From the documentation get_or_create:
# get_or_create() a person with similar first names.
p, created = Person.objects.get_or_create(
first_name='John',
last_name='Lennon',
defaults={'birthday': date(1940, 10, 9)},
)
# get_or_create() didn't have to create an object.
>>> created
False
Explanation:
Fields to be evaluated for similarity, have to be mentioned outside defaults. Rest of the fields have to be included in defaults. In case CREATE event occurs, all the fields are taken into consideration.
It looks like you need to be returning into a tuple, instead of a single variable, do like this:
customer.source,created = Source.objects.get_or_create(name="Website")

get_or_create returns a tuple.
customer.source, created = Source.objects.get_or_create(name="Website")

get_or_create() returns a tuple:
customer.source, created = Source.objects.get_or_create(name="Website")
created → has a boolean value, is created or not.
customer.source → has an object of get_or_create() method.

Following #Tobu answer and #mipadi comment, in a more pythonic way, if not interested in the created flag, I would use:
customer.source, _ = Source.objects.get_or_create(name="Website")

The issue you are encountering is a documented feature of get_or_create.
When using keyword arguments other than "defaults" the return value of get_or_create is an instance. That's why it is showing you the parens in the return value.
you could use customer.source = Source.objects.get_or_create(name="Website")[0] to get the correct value.
Here is a link for the documentation:
http://docs.djangoproject.com/en/dev/ref/models/querysets/#get-or-create-kwargs

get_or_create method would actually return a tuple.
The trick with the get_or_create method is that it actually returns a tuple of (object, created). The first element is an instance of the model you are trying to retrieve and the second is a boolean flag to tell if the instance was created or not. True means the instance was created by the get_or_create method and False means it was retrieved from the database
So you can do something like to get the source instance
``` customer.source = Source.objects.get_or_create(name="Website")[0]
```

Important warning.
you should take care of the following before using the get_or_create , https://docs.djangoproject.com/en/4.0/ref/models/querysets/.
....
Warning
This method is atomic assuming that the database enforces uniqueness of the keyword arguments (see unique or unique_together). If the fields used in the keyword arguments do not have a uniqueness constraint, concurrent calls to this method may result in multiple rows with the same parameters being inserted.

Related

Django how to query objects one by one

I have to query an object from a model, this object is called "exercise" and has many fields(title, body, answers, etc.) I need to get "exercise objects" one by one with all their fields, do some rendering on them and give back a pdf of the exercise as result.
The problem is, if I do:
exercises = ExerciseModel.objects.all()
I get all of them. If I do:
some_exercises = ExerciseModel.objects.filter(something=something)
I get "some of them" depending on filter. And if i do:
exercise = ExerciseModel.objects.get()
I get
error get() returned more than one exercise
How can I get them one by one? I must be able to use them as input for another function.
If you need to perform the task on only 1 exercise, use get() instead. In such case, you need a criteria to make sure get() will return 1 and only 1 result.
Example:
ex = ExerciseModel.objects.get(pk=123)
From the doc:
Returns the object matching the given lookup parameters, which should be in the format described in Field lookups.
get() raises MultipleObjectsReturned if more than one object was found. The MultipleObjectsReturned exception is an attribute of the model class.
get() raises a DoesNotExist exception if an object wasn’t found for the given parameters. This exception is an attribute of the model class.
When you have a QuerySet with filters that ensure the underlying SQL request will return only 1 row, you can call get() without argument on the QuerySet. This return the row as model instance, instead of returning it as a list containing 1 elemnt.
Original answer:
Both filter() and all() methods return a QuerySet instance. You can iterate on it to perform a task for each "exercise" returned by your request
for exercise in ExerciseModel.objects.filter(something=something):
# ... do what you need on your exercise
From the documentation:
A QuerySet is iterable, and it executes its database query the first time you iterate over it. For example, this will print the headline of all entries in the database
What you need is .iterator() which will turn a queryset to an iterator:
exercises = ExerciseModel.objects.all().iterator()
then you get iterate over it in a for loop:
for exercise in exercises:
...
This will improve performance when you have large number of items. However, it has some downsides as well as stated in the docs
Hope it helps!
.get() must return a single object, not a QuerySet instance. If you want to get a single exercise then you must pass a parameter to the .get instance. For example, retrieving an object by name would require the following code:
exercise = ExerciseModel.objects.get(name="an_exercise")
If you want to iterate through all the objects without actually retrieving a QuerySet containing the objects, you could use the following code:
for i in range(Exercise.objects.all().count()):
exercise = Exercise.objects.get(pk=i)
... operate on object ...

django pass field-name as variable in get_or_create

I am trying to see if I can pass field name as a variable in get_or_create (since I have a function where the key in the kwargs can vary)
Like so:
def convert_value(cell_value, field_to_lookup):
rem_obj, created = Rem.objects.get_or_create(field_to_lookup=cell_value)
print ('created? ',created)
return rem_obj
The above wont work since it would look for 'field_to_lookup' as the key.
This post suggests using getattr but not sure if that'll be applicable in this case since I will again need to assign the output to a variable
This post helped. Now passing the field-value pair as dict which allows passing variables for field names. Here's the code:
def convert_value(cell_value, field_to_lookup):
rem_obj, created = Rem.objects.get_or_create(**{field_to_lookup:cell_value})
print ('created? ',created)
return rem_obj
Alternatively, I could directly just pass the dict to the function.

What is the best way to get an object which is not sure if there is or not in Django model

What is the best way to get an object which is not sure if there is or not.(in Django model)
I think there are three options.
One is to use try and except statement to process the exception.
Second is to use objects.filter() instead of objects.get() and check query_set.count().
Third is to use objects.count() first and then use objects.get() if there is.
You can use .first() operation on a queryset to your advantage to get the first object of the queryset if it exists and None otherwise.
Returns the first object matched by the queryset, or None if there is
no matching object.
my_object = some_queryset.first() # return a object or 'None'
This will return the first object of the queryset if there are objects in the queryset. Otherwise, it returns None. It will automatically handle the case when objects do no exist without you needing to write try-except.
The above code is equivalent to writing:
try:
my_object = some_queryset[0] # access the first element
except IndexError:
my_object = None
Note: Here, we needed the object, so we are using .first(). If we needed to check only if the object exists, then we could have used .exists()
Edited: I think the best solution in this case, is use this third package
django-annoying
This packaged contains a lot of syntactic sugar for things like that.
With this package, you can use the function get_object_or_None, similar to
get_object_or_404.
But, the real code behind this function is :
def get_object_or_None(klass, *args, **kwargs):
"""
Uses get() to return an object or None if the object does not exist.
klass may be a Model, Manager, or QuerySet object. All other passed
arguments and keyword arguments are used in the get() query.
Note: Like with get(), a MultipleObjectsReturned will be raised if more than one
object is found.
"""
queryset = _get_queryset(klass)
try:
return queryset.get(*args, **kwargs)
except queryset.model.DoesNotExist:
return None
Note: Don’t use this if all you want to do is determine if at least one result exists. It’s more efficient to use exists().

get_or_create: is it a 'get' or a 'create'?

AFAIK, peewee's Model.get_or_create() doesn't return a flag that indicates a creation, unlike django's get_or_create(). Is there a good way to check if an instance returned by get_or_create() is freshly created?
Thanks
There's a section in the docs that should hopefully be helpful: http://docs.peewee-orm.com/en/latest/peewee/querying.html#get-or-create
If the docs are lacking, please let me know and I'll be happy to improve them.
http://docs.peewee-orm.com/en/latest/peewee/api.html#Model.get_or_create
classmethod get_or_create(**kwargs)
Attempt to get the row matching the given filters. If no matching row is found, create a new row.
Parameters:
kwargs – Mapping of field-name to value.
defaults – Default values to use if creating a new row.
Returns:
Tuple of Model instance and boolean indicating if a new object was created.
It also warns you that race conditions are possible with this method, and even gives you an example without using the method:
try:
person = Person.get(
(Person.first_name == 'John') &
(Person.last_name == 'Lennon'))
except Person.DoesNotExist:
person = Person.create(
first_name='John',
last_name='Lennon',
birthday=datetime.date(1940, 10, 9))
According to source code, no way to find out. Also, according to documentation, it is not recommended to use this method.
I suggest to use try/except/else clause.

Cannot assign "(<Blog: title1>, False)": "Post.blog" must be a "Blog" instance [duplicate]

I'm trying to use get_or_create for some fields in my forms, but I'm getting a 500 error when I try to do so.
One of the lines looks like this:
customer.source = Source.objects.get_or_create(name="Website")
The error I get for the above code is:
Cannot assign "(<Source: Website>, False)": "Customer.source"
must be a "Source" instance.
From the documentation get_or_create:
# get_or_create() a person with similar first names.
p, created = Person.objects.get_or_create(
first_name='John',
last_name='Lennon',
defaults={'birthday': date(1940, 10, 9)},
)
# get_or_create() didn't have to create an object.
>>> created
False
Explanation:
Fields to be evaluated for similarity, have to be mentioned outside defaults. Rest of the fields have to be included in defaults. In case CREATE event occurs, all the fields are taken into consideration.
It looks like you need to be returning into a tuple, instead of a single variable, do like this:
customer.source,created = Source.objects.get_or_create(name="Website")
get_or_create returns a tuple.
customer.source, created = Source.objects.get_or_create(name="Website")
get_or_create() returns a tuple:
customer.source, created = Source.objects.get_or_create(name="Website")
created → has a boolean value, is created or not.
customer.source → has an object of get_or_create() method.
Following #Tobu answer and #mipadi comment, in a more pythonic way, if not interested in the created flag, I would use:
customer.source, _ = Source.objects.get_or_create(name="Website")
The issue you are encountering is a documented feature of get_or_create.
When using keyword arguments other than "defaults" the return value of get_or_create is an instance. That's why it is showing you the parens in the return value.
you could use customer.source = Source.objects.get_or_create(name="Website")[0] to get the correct value.
Here is a link for the documentation:
http://docs.djangoproject.com/en/dev/ref/models/querysets/#get-or-create-kwargs
get_or_create method would actually return a tuple.
The trick with the get_or_create method is that it actually returns a tuple of (object, created). The first element is an instance of the model you are trying to retrieve and the second is a boolean flag to tell if the instance was created or not. True means the instance was created by the get_or_create method and False means it was retrieved from the database
So you can do something like to get the source instance
``` customer.source = Source.objects.get_or_create(name="Website")[0]
```
Important warning.
you should take care of the following before using the get_or_create , https://docs.djangoproject.com/en/4.0/ref/models/querysets/.
....
Warning
This method is atomic assuming that the database enforces uniqueness of the keyword arguments (see unique or unique_together). If the fields used in the keyword arguments do not have a uniqueness constraint, concurrent calls to this method may result in multiple rows with the same parameters being inserted.

Categories