Create an object that has a ForeignKey with another object - django

Assume that I have two models:
class Category(models.Model):
title = models.CharField(max_length=255)
class Product(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
title = models.CharField(max_length=255)
What I wanna accomplish is creating a Product object.
Assume I'll receive category_id from the POST request
>>> category = Category.objects.get(id=category_id)
>>> Product.objects.create(category=category, title='Send the category object')
>>> Product.objects.create(category_id=category_id, title='Send only category id')
As you can see, there are two options, the first is to send category instance to create() method and the second is to send category_id, so my question is which approach is better for performance?
I know that I need to check if that category exists in the DB or not but this is not the case that I'm talking about in the question.

If you have the primary key (category_id) there is no need to fetch the category first, you thus can use:
Product.objects.create(category_id=category_id, title='Send only category id')
which will thus only make one query: a query to create the Product, and thus will avoid querying for the category.
The two .create(…)s will make exactly the same query since Django will simply retrieve the .id of the category object, and make the query with that id.
I know that I need to check if that category exists in the DB or not but this is not the case that I'm talking about in the question.
No, if one would make a request with a category_id for which there is no record for the table behind the Category model, it will raise an IntegrityError: the database will normally ensure that ForeignKeys always point to valid items, so the Category.objects.get(id=category_id) is not necessary: if you are only fetching it to fetch the category, you can omit that.

Related

update field with number of row in another table django

I'm building a web crawler contain links blogs etc of x website ... , I have field called number_of_crawled_Links and I want to make the value of that filed is the number of rows in another model Where Links Stored i want the process to be automatically without making request any idea how to do that
You cannot do that in fields directly, but it's good idea to do that as a method.
class ModelA(models.Model):
some_field = models.TextField()
class ModelB(models.Model):
model_a = models.ForeignKey(ModelA, on_delete=models.CASCADE)
def get_some_field_value(self):
return self.model_a.some_field
Then ModelB can get dynamically the value of ModelA field some_field. You can do it with any type of values.

Django Query ManyToMany with Custom Through Table Field Data

I've been trying to figure this one out for a while now but am confused. Every ManyToMany relationship always goes through a third table which isn't that difficult to understand. But in the event that the third table is a custom through table with additional fields how do you grab the custom field for each row?
Here's a sample table I made. How can I get all the movies a User has watched along with the additional watched field and finished field? This example assumes the user is only allowed to see the movie once whether they finish it or not so there will only be 1 record for each movie they saw.
class Movie(models.Model):
title = models.CharField(max_length=191)
class User(models.Model):
username = models.CharField(max_length=191)
watched = models.ManyToMany(Movie, through='watch')
class Watch(models.Model):
user = models.Foreignkey(User, on_delete=models.CASCADE)
movie = models.Foreignkey(Movie, on_delete=models.CASCADE)
watched = models.DateTimeField()
finished = models.BooleanField()
Penny for your thoughts my friends.
You can uses:
from django.db.models import F
my_user.watched.annotate(
watched=F('watch__watched'),
finished=F('watch__finished')
)
This will return a QuerySet of Movies that contain as extra attributes .watched and .finished.
That being said, it might be cleaner to just access the watch_set, and thus iterate over the Watch objects and access the .movie object for details about the movie. You can use .select_related(..) [Django-doc] to fetch the information about the Movies in the same database query:
for watch in my_user.watch_set.select_related('movie'):
print(f'{watch.movie.title}: {watch.watched}, {watch.finished}')

How to save a Foreign Key in Django without instance, but a hard coded value

Suppose I have these 2 models:
class Shipment(models.Model):
id = models.BigIntegerField(primary_key=True)
order_id = models.ForeignKey('orders.Order', null=True, blank=True, on_delete=models.SET_NULL)
class Order(models.Model):
id = models.BigIntegerField(primary_key=True)
ean = models.BigIntegerField(null=True, blank=True)
The orders are already populated in the database, now I just want to link the shipments to the related order. But I have a list of hardcoded JSON to populate my shipment model.
{
"shipmentId": 541757635,
"orderId": 23598235,
}
So the orderId in that JSON represents the primary key of a Order model that is already present in the database. How can I loop over this to connect the shipment model with the correct order model based on the order_id?
Maybe a loop like this:
for shipment in shipments:
shipment = Shipment.objects.create(id=shipment.shipmentId, order_id=shipment.orderId
But is this possible because there is no instance, just a hard coded value in the JSON?
UPDATE:
So when I try to use this loop I get this error:
self.field.remote_field.model._meta.object_name,
ValueError: Cannot assign "2647598550": "Shipment.order_id" must be a "Order" instance.
Your example loop should do it. You can specify the ID to be anything you want, as long as you do it before calling .save(). Building the instance with .create(id=foo) should do exactly that.
To set a foreign key field, you can first get the related instance using the id with Order.objects.get(id=order_id).
Example code:
for shipment in shipments:
order = Order.objects.get(id=shipment.orderId)
shipment = Shipment.objects.create(id=shipment.shipmentId,
order_id=order)
Related: Django error. Cannot assign must be an instance
Source: https://docs.djangoproject.com/en/2.2/ref/models/instances/#explicitly-specifying-auto-primary-key-values

select_related with reverse foreign keys

I have two Models in Django. The first has the hierarchy of what job functions (positions) report to which other positions, and the second is people and what job function they hold.
class PositionHierarchy(model.Model):
pcn = models.CharField(max_length=50)
title = models.CharField(max_length=100)
level = models.CharField(max_length=25)
report_to = models.ForeignKey('PositionHierachy', null=True)
class Person(model.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
...
position = models.ForeignKey(PositionHierarchy)
When I have a Person record and I want to find the person's manager, I have to do
manager = person.position.report_to.person_set.all()[0]
# Can't use .first() because we haven't upgraded to 1.6 yet
If I'm getting people with a QuerySet, I can join (and avoid a second trip to the database) with position and report_to using Person.objects.select_related('position', 'position__reports_to').filter(...), but is there any way to avoid making another trip to the database to get the person_set? I tried adding 'position__reports_to__person_set' or just position__reports_to__person to the select_related, but that doesn't seem to change the query. Is this what prefetch_related is for?
I'd like to make a custom manager so that when I do a query to get Person records, I also get their PositionHeirarchy and their manager's Person record without more round trips to the database. This is what I have so far:
class PersonWithManagerManager(models.Manager):
def get_query_set(self):
qs = super(PersonWithManagerManager, self).get_query_set()
return qs.select_related(
'position',
'position__reports_to',
).prefetch_related(
)
Yes, that is what prefetch_related() is for. It will require an additional query, but the idea is that it will get all of the related information at once, instead of once per Person.
In your case:
qs.select_related('position__report_to')
.prefetch_related('position__report_to__person_set')
should require two queries, regardless of the number of Persons in the original query set.
Compare this example from the documentation:
>>> Restaurant.objects.select_related('best_pizza')
.prefetch_related('best_pizza__toppings')

Django model clean and ManytoMany problem

I want to check my Foo instance if it belongs to a category yet my category is a many to many attribute thus I get "'Foo' instance needs to have a primary key value before a many-to-many relationship can be used." once I try such:
class Category(models.Model):
name = models.CharField(max_length=120)
class Movie (models.Model):
poster = models.ImageField(blank=True)
categories = models.ManyToManyField(Category)
def clean(self):
try:
self.categories.all().get(db_name="special")
self.poster.url = u'/media/special_img.png'
except Category.DoesNotExist:
pass
You can't check if Foo belongs to a category yet until you save it. But since it isn't saved anyway, you can be sure that it doesn't belong to a category yet.
One way to check wheter it has been saved already is by using if foo.pk. The pk is the primary key and it will be None if it hasn't been saved yet.