Annotate and Aggregate function in django - django-models

In django I have the following tables and am trying to count the number of votes by item.
class Votes(models.Model):
user = models.ForeignKey(User)
item = models.ForeignKey(Item)
class Item(models.Model):
name = models.CharField()
description = models.TextField()
I have the following queryset
queryset = Votes.objects.values('item__name').annotate(Count('item'))
that returns a list with item name and view count but not the item object. How can I set it up so that the object is returned instead of just the string value? I have been messing around with Manager and Queryset methods, that the right track? Any advice would be appreciated.

You can try something this:
queryset = Votes.values.annotate(t_count=Count('item'))
To get the count value of first Vote object:
queryset[0].t_count
or to get Item object:
Item.objects.annotate(i_count=Count('votes'))

Related

How to add different many-to-many field into a single instance or row of a foreign key in django-rest-framework using model serializer

I am creating an ecommerce website using Django-rest-framework and react. I am trying to add items to cart. I am able to add items to the cart on the frontend but I want to store the cart data to the backend (Django) database so that whenever the user add items to cart and reloads the page the items should still be in his cart like any other ecommerce site. Here is my Code for django models, serializers, viewset.
class Products(models.Model):
title = models.CharField(max_length=100)
image = models.URLField()
description = models.TextField(max_length=500, blank=True, null=True)
category = models.CharField(max_length=50, blank=True, null=True)
rating = models.IntegerField(blank=True, null=True)
price = models.FloatField()
def __str__(self):
return f"{self.id}"
class Cart(models.Model):
product = models.ManyToManyField(
Products, related_name="cart")
buyer = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="cart")
def __str__(self) -> str:
return f"{self.buyer}"
class ProductsSerializer(serializers.ModelSerializer):
class Meta:
model = Products
fields = '__all__'
class CartSerializer(serializers.ModelSerializer):
class Meta:
model = Cart
fields = '__all__'
class ProductsViewSet(viewsets.ModelViewSet):
queryset = Products.objects.all()
serializer_class = ProductsSerializer
class CartViewSet(viewsets.ModelViewSet):
queryset = Cart.objects.all()
authentication_classes = [JWTAuthentication]
permission_classes = [
permissions.IsAuthenticated
]
serializer_class = CartSerializer
router = routers.DefaultRouter()
router.register('products', ProductsViewSet, 'products')
router.register('cart', CartViewSet, 'cart')
I am using postman to post the cart items. I am able to add more than one products for a single buyer.
but the problem is when i again add another product to the same user using postman i added before, it creates another row for the same user.
I do not want that. I want a single instance or row of a user in cart table and add as many products as i want. when i post other products for the same user, that product should also get added up in the single user row or instance. What is the best way to achieve my goal.
Here is the issue, Django can't automatically do that because it doesn't know which behaviour is expected. If you look at the code to add a product and look at the code to add a cart, it's exactly the same. So behaviour will also be the same.
For the behaviour that you want, you will have to override the create method of your ModelViewSet.
Here are the steps to achieve what you want -
Check whether or not the user with that id already has a cart.
If they have a cart, then you'll need to fetch the cart object belonging to that user and add products to it.
If they don't, then you'll have to create a new cart object and do the default thing.
class CartViewSet(viewsets.ModelViewSet):
queryset = Cart.objects.all()
authentication_classes = [JWTAuthentication]
permission_classes = [
permissions.IsAuthenticated
]
serializer_class = CartSerializer
def create(self, request):
# Checking whether the Cart object already exists for the buyer
cart = Cart.objects.filter(buyer_id = request.data.get("buyer"))
if len(cart)=1:
#If cart exists, we loop through the list of product ids and add them
for product_id in request.data.get("product"):
cart[0].product.add(get_object_or_404(Product, id = product_id ))
if len(cart)=0:
# Doing what normally happens.
return super().create(request)
if len(cart)>1:
#Error in database. One person having multiple carts. Custom error message.
To check out how to add data to many-to-many fields, check this out.

Django Rest returning related items for each item

I was trying to find answer in similiar questions, but none was meeting my expectations.
I have 2 models:
class Artist(models.Model):
name = models.CharField(max_length=255)
music_type = models.CharField(max_lenght=50)
def __str__(self):
return self.name
class Event(models.Model):
event_name = models.CharField(max_length=255)
...
artists = models.ManyToManyField(Artist)
def __str__(self):
return self.event_name
I also have serializers.py file:
class EventSerializer(serializers.ModelSerializer):
class Meta:
model = Event
fields = '__all__'
class ArtistSerializer(serializers.ModelSerializer):
events = EventSerializer(source='event_set', many=True)
class Meta:
model = Artist
fields ='__all__'
The event in ArtistSerializer allows me to return all events where artist takes part.
Now for each artist I would like to get list of all artists if they ever were taking part in the same event.
For example I have 5 Artists (A1...A5) and 3 Events (E1...E3)
In Event 1: [A1,A3]
In Event 2: [A3,A4,A5,A2]
In Event 3: [A2, A3]
So for A3 I would like to get list [A1,A4,A5,A2]
For A1: [A3]
For A2: [A3,A4,A5]
Unfortunately I have huge problem to create this query as SQL-query and ORM mechanism looks more complicated in this situation. Can somebody help me with this problem or give hints how to solve this?
If it's needed I'm gonna share more code
You can query the through model to get the artists related to an event. This is the intermediate model which django will have created to make that M2M relationship.
Where you have a ManyToManyField it has an attribute of through which is the M2M model.
So from your event model you could do something like Event.artists.through.objects.all() and you'd see all the instances in your M2M model.
So to find out the artists which are linked to a given event you could query that same table;
Event.artists.through.objects.filter(event_id=1).select_related('artist')
This would then return all the objects in the M2M which belong to Event 1. You could then get the artists from there, or just grab the artist IDs Event.artists.through.objects.filter(event_id=1).values_list('artist_id, flat=True)
Given the scenario in your comment...
If you have an artist, then you can run a query to get the events they've been in, and then run another query with those event ids. In that second query you are then looking to get the artist ids that aren't the current artist you're already looking at.
# First get the events that the current artist has been in
event_ids = Event.artists.through.objects.filter(artist_id=1).values_list('event_id, flat=True)
# Then get the other artists who have been in the same events
other_artist_ids = Event.artists.through.objects.filter(event_id__in =event_ids).exclude(artist_id=1).values_list('artist_id, flat=True)
# or the full instances
other_artists = Event.artists.through.objects.filter(event_id__in =event_ids).exclude(artist_id=1).select_related('artist')

Filter queryset results based on a boolean field which is True

class User(models.Model):
name = models.EmailFiled()
class Product(models.Model):
title = models.CharField(max_length=50)
is_active = models.BooleanField(default=True)
class Cart(models.Model):
product = models.ManyToManyField(Product)
class Order(models.Model):
user = models.ForeignKey(User,on_delete=models.CASCADE)
cart = models.ForeignKey(Cart,on_delete=models.CASCADE)
We have multiple products. some of them are active=False the other ones are True. I should take the products which is active=true requesting user.
You are looking for the ability to filter results of a ViewSet based on your active flag. I highly recommend you to read DRF Filtering documentation
You simply need to add to your ViewSet or APIView the following fields
from django_filters.rest_framework import DjangoFilterBackend
class ProductList(generics.ListAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
# Add Filtering Backend
filter_backends = [DjangoFilterBackend]
# Add filtering fields (Default behavior is to exact match provided values)
filterset_fields = ['is_active']
and now in your request, you should have the query param
http://example.com/api/products?is_active=1
You can obtain the Products that are active and in the Cart of a user with:
Product.objects.filter(is_active=True, cart__order__user=my_user)

With django-tables2 how do I display a model #property?

I have a #property in my model which is basically a string of several values in the model combined.
#property
def mfg_link(self):
return ''.join('http://mfg.com/model/' + str(self.model_numer))
I can add this mfg_link to the list_display on the admin model and it works fine, but its not showing up in the generated table when I pass the queryset to the table, the other fields show up fine.
This seems like something obvious, but unfortunately the couple of hours of searching didn't help.
Thanks a lot!
Do it like this in Table class:
class TableClass(tables.Table):
mfg_link = tables.Column('mfg_link', order_by='model_numer')
class Meta:
model = SomeModel
sequence = 'model_numer', 'mfg_link'

GAE - retrieving the last entry (python)

I am trying to get the most recent data item from the datastore.
I am trying to apply the method as explained here but I am getting the object <__main__.Rep object at 0x075F1730> not the item. Can anyone explain what I am doing wrong?
The Model is:
class Rep(db.Model):
sent = db.StringProperty()
time = db.DateTimeProperty(auto_now_add=True)
This is the handler:
class Repeater(webapp.RequestHandler):
def get(self):
reps = Rep()
reps.sent = self.request.get('sentence')
reps.put()
s = self.request.get('sentence')
query = reps.all()
last = query.order('-time').get()
sentences = query
Thanks!
I don't see anything wrong at all.
<__main__.Rep object at 0x075F1730> is presumably an instance of your Rep class, as expected.

Resources