How to create relationships between 3 models in django? - django

I'm building a marketplace that have 2 user types i.e. buyers and sellers.
Now I want to create a relationship model between buyers, sellers and OrderedItems, so that the sellers gets notified once the buyers orders an item.
This is the model I made:
class Ordered(models.Model):
ordered_items = models.ManyToManyField(Cart)
seller = models.ForeignKey(SellerProfile, on_delete = models.SET_NULL, null = True)
buyer = models.ForeignKey(CustomerProfile, on_delete = models.CASCADE)
ordered_on = models.DateTimeField(auto_now_add = True)
But I don't know how to implement this in views.py
Any suggestion will be really helpful
Thank you

As per the current model relation you can use pre_save or post-save signals on ordered model. Whenever you update a new value(item added by buyer) in ordered_items(many-to-many relation) you can send a mail to seller linked with that buyer. That mailing part will be defined inside pre or post signals .
In views.py you will get input parameters as follows -:
seller_id
ordered_item
buyer_id
And you will search for seller_id and buyer_id combination using filter statement in Ordered model and set ordered_item in that field. After that model save function will be called and after that post_save signal will be triggered and will send the mail to seller.
There will be 2 conditions for this scenario -:
Either you create a new value if user is buying his/her first item.
Buyer is adding another item in cart. In this case you will update the existing value.
In Either case model's save function will automatically be initiated.
Reference -:
Pre-save and Post-save signals in django
Django Official Documentation on signals

Related

model relationship and Queries in Django not a clear

what does these lines of code mean in Django View: i couldn't find a details explanation, I came to Django from a Laravel background, so I can understand the models and relationships... thanks
customer = request.user.customer
product = Product.objects.get(id=productId)
order, created = Order.objects.get_or_create(customer=customer, complete=False)
orderItem, created = OrderItem.objects.get_or_create(order=order, product=product)
customer = request.user.customer
The request object has a user, the user is the authenticated user (if no user is authenticated then the AnonymousUser object is returned instead). In this example the User model (i.e. the user table) has a field called customer and we are accessing that field.
product = Product.objects.get(id=productId)
Here we are simply querying the Product table for a specific product with the given productId. Note, Django will raise an error if two records are returned when you use the .get() method (i.e. if two rows in the Product table have the same productId.
order, created = Order.objects.get_or_create(customer=customer, complete=False)
Next we use the get_or_create() method to look up an order based off of the customer (the value of which we extracted above. If an order cannot be found we will create one instead. The value of createdwill be True if a neworder` was created or False if one already existed.
orderItem, created = OrderItem.objects.get_or_create(order=order, product=product)
Just as above we are getting or creating an OrderItem using the order and product fields.

Select latest record in the group with ordering

I am having trouble writing a query using Django ORM, I want to find the latest record in each group. I am putting chat messages in the model and I want to find the latest chat of each user and show chats latest chat of each user and with the latest user's chat on the home screen just like in WhatsApp, Skype or similar apps. Currently, I am using the following query,
Chats.objects.all().order_by('user_id', '-date').distinct('user_id')
Using this I am able to get the latest chat of each user but I am not able to get the sequence correct. The result of the query is in the order of which the users were created in the database which I understand is correct, but I want to show the user who sent the latest chat at the top.
My Models.py
class Chats(models.Model):
user_id = models.ForeignKey(User, on_delete=models.CASCADE)
chat = models.CharField(max_length=1023, null=True, blank=True)
date = models.DateTimeField(auto_now_add=True)
Thank you so much, Please let me know if any other information is required.
Option 1: Order on the Django/Python layer
The items are first sorted by user_id, and only in case of a tie, it takes the one with the latest date. But that means that you eventually get for each user a Chats object, ordered by the user_id.
I think here your only option is to sort it at the Django/Python level, so wrap it into a list, and sort by the date:
from operator import attrgetter
items = list(Chats.objects.order_by('user_id', '-date').distinct('user_id'))
items.sort(key=attrgetter('date'), reverse=True)
# work with items
and then render the items in the template.
Option 2: Annotate the User model instead
Another option is to annotate the User model and thus work with a QuerySet of User objects:
from django.db.models import Max, OuterRef, Subquery
User.objects.filter(
chats__isnull=False
).annotate(
last_date=Max('chats__date'),
last_message=Subquery(
Chat.objects.filter(user_id=OuterRef('pk')).order_by('-date').value('chat')[:1]
)
).order_by('-last_date')
Here the User objects will have an extra attribute .last_date with the latest date time of the object, and .last_message with that message.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

How can I access a value from one model and pass to another model in Django?

I have one model called Weight (filled by User input/choice) and another called Enterprise.
class Weight(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="weights")
weight_of_history = models.IntegerField(null=True, blank=True)
class Enterprise(models.Model):
...
The weight is saved, one per user, and replaced everytime the user choose new one.
Inside the Enterprise class, I am creating an property that must get the "weight_of_history" (depending on the user, who has chosen the weight) from Weight class, but the models have no Foreign key or related name between them.
class Enterprise(models.Model):
...
#property
def ranking(self):
weight_of_history = <-- HERE I NEED TO TAKE WEIGHT_HISTORY FROM THE FIRST MODEL
THEN I COULD CALCULATE
How could I do that? Thank you!
You can use django's powerful query functionality and fetch the required objects from the db. Here are the docs that might help you with that. Django docs are amazing, so I would recommend that you read up on queries, models, and forms to have easier time with you project.
For your example we can fetch all the weights for the user in one query by filtering the weights by user. In fact, django ORM allows for chaining filters and you can create really sophisticated queries
class Enterprise(models.Model):
...
#property
def ranking(self):
weight_of_history = Weight.objects.filter(user=some_user)
If you do not know the user beforehand, then you can do inside the view and grab the user that makes the request and filter using this user:
#views.py
user_making_request = request.user
weight_of_history = Weight.objects.filter(user=user_making_request)

Adding to List Style Entries in Django Admin

I would like to create a model where the user can add objects of a model to a list in Django.
For example:
class Receipt(models.Model):
items = #many to many maybe? but their is only one receipt for each item object
#other fields
class Item(models.Model):
name = model.CharField()
#otherfields
Class ItemPurchase(models.Model):
receipt = model.ForeignKey(Receipt)
item = model.ForeignKey(Item)
quantity = model.IntegerField()
#other fields specific to this Receipt
Then in the admin, I want to allow the user to add Item objects to their current Receipt objects in the admin panel, and create an ItemPurchase record, but how would I best do this? The Receipt cannot be associated until the Receipt is made...
Turns out Django's inline feature combined with custom queryset returns is exactly what I needed to do everything I wanted, thanks #Hedde for the tip!
Read about inlines:
https://docs.djangoproject.com/en/dev/ref/contrib/admin/#inlinemodeladmin-objects
This helped in multiple ways, I just needed a push in the right direction:
formfield_for_foreignkey and Inline Admin

Django - How to compare pre / post save state of a model including all relations

I am trying to maintain a few sets in redis that track models in django
class Campaign(models.Model):
advertiser = models.ForeignKey(User)
name = models.CharField(max_length=200)
carriers = models.ManyToManyField(Carrier)
countries = models.ManyToManyField(Country)
#receiver(pre_save, sender=Campaign)
def adserver_clear_cache(sender, **kwargs):
campaign = kwargs['instance']
for con in campaign.countries.all():
r.srem("con:" + str(con.id), campaign.id)
for car in campaign.carriers.all():
r.srem("car:" + str(car.id), campaign.id)
#receiver(post_save, sender=Campaign)
def adserver_save_cache(sender, **kwargs):
campaign = kwargs['instance']
for con in campaign.countries.all():
r.sadd("con:" + str(con.id), campaign.id)
for car in campaign.carriers.all():
r.sadd("car:" + str(car.id), campaign.id)
The issue is, the campaign is fully saved, before each of the carriers, campaigns and such are saved. So I am getting the same data in pre + post_save. Is there a way to call a function when everything, including relations are finished saving?
thanks!
The problem is that the m2m relationship is saved after your model is saved, in other words "post_save() is still too early" for what you're after.
You can either use the m2m_changed signal as jpic points out, or use Django 1.4's new "save_related" - https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.save_related which closed ticket 16115 https://code.djangoproject.com/ticket/16115
See also https://stackoverflow.com/a/8462541/640759
ManyToManyField is special. It does not represent in the database by a column of its model's table. In fact, a ManyToManyField represents as another table that links the models at both sides of the n:m relation.
It does not make sense to emit a model signal such as post_save for an arbitrary side of the n:m relation. When a ManyToManyField changes, m2m_changed signal is emited:
Sent when a ManyToManyField is changed on a model instance. Strictly speaking, this is not a model signal since it is sent by the ManyToManyField, but since it complements the pre_save/post_save and pre_delete/post_delete when it comes to tracking changes to models, it is included here.