Django search with AND operator many to many relationship - python-3.x

This is my model
class MenuItem(models.Model):
name = models.CharField(max_length=500, null=False)
description = models.CharField(max_length=500, null=True)
image_url = models.CharField(max_length=1000, null=True)
menu_category = models.ForeignKey(MenuCategory, on_delete=models.CASCADE)
def __str__(self):
return f'{self.name}'
class Venue(models.Model):
name = models.CharField(max_length=500, null=False)
def __str__(self):
return f'{self.name}'
class VenueMenu(models.Model):
venue = models.ForeignKey(Venue, null=False, on_delete=models.CASCADE)
menu_item = models.ManyToManyField(MenuItem, null=False)
This is my view
#api_view(['GET'])
def search_menu_item(request):
if request.GET.get('venue') and request.GET.get('search_name'):
menu_item_filter = Q(menu_item__name__icontains=request.GET.get('search_name'))
venue_filter = Q(venue__name=request.GET.get('venue').title())
menu_item_search = VenueMenu.objects.filter(venue_filter & menu_item_filter)
serializer = VenueMenuSerializer(menu_item_search, many=True)
return Response(serializer.data)
This is my serializer
class MenuItemSerializer(serializers.ModelSerializer):
menu_category = MenuCategorySerializer(many=True)
class Meta:
model = MenuItem
fields = '__all__'
class VenueSerializer(serializers.ModelSerializer):
class Meta:
model = Venue
fields = '__all__'
class VenueMenuSerializer(serializers.ModelSerializer):
menu_item = MenuItemSerializer(many=True)
venue = VenueSerializer(many=False)
I want to search a single menu item information in a specific venue, as you can see in my query am querying the object but this query is returning me all the menu items associated with that venue including the one which I have searched but I want to have a single menu item which I am searching associated to that venue.

You get the correctly filtered VenueMenus, but the related MenuItems aren't filtered automatically because the filter is for VenueMenu and not MenuItem.
To filter the related MenuItems in the serializer, you'll have to do a filtered prefetch using Prefetch like so:
from django.db.models import Prefetch
menu_item_search = VenueMenu.objects.filter(
venue_filter & menu_item_filter
).prefetch_related(
Prefetch(
'menu_item',
queryset=MenuItem.objects.filter(name__icontains=request.GET.get('search_name'))
)
)
Also to improve this further, you can also select the related Venue using select_related to avoid doing a separate query just to get the venue details in the serializer. So all in all:
menu_item_search = VenueMenu.objects.filter(
venue_filter & menu_item_filter,
).select_related(
'venue',
).prefetch_related(
Prefetch(
'menu_item',
queryset=MenuItem.objects.filter(name__icontains=request.GET.get('search_name'))
)
)

Related

How to send Django model object's details as answer according to the user's choice of InlineKeyboardButton?

I'm new to creating telegram bots with Django. I selected python-telegram-bot library for creating bots with Django.
I've created a Django model, named as category:
class category(models.Model):
name = models.CharField(max_length=250)
category_id = models.IntegerField()
def __str__(self):
return self.name
and product:
class product(models.Model):
product_category = models.ForeignKey(category, on_delete=models.SET_NULL, blank=True, null=True)
name = models.CharField(max_length=250)
cost = models.FloatField()
img = models.ImageField()
def __str__(self):
return self.name
Successfully created InlineKeyboardButtons and placed id of each product model object to each button, with these functions:
def product(update: Update, context: CallbackContext):
query = update.callback_query.data
product_button_list = []
for each in product.objects.select_related().filter(product_category_id = query):
product_button_list.append(InlineKeyboardButton(each.name, callback_data=each.id))
reply_markup = InlineKeyboardMarkup(build_menu(product_button_list,n_cols=2))
update.callback_query.message.edit_text("Mahsulotni tanlang!", reply_markup=reply_markup)
def build_menu(buttons,n_cols,header_buttons=None,footer_buttons=None):
menu = [buttons[i:i + n_cols] for i in range(0, len(buttons), n_cols)]
if header_buttons:
menu.insert(0, header_buttons)
if footer_buttons:
menu.append(footer_buttons)
return menu
When the user selects a button named as the name of the product model object, I am receiving the correct id of the product model object, tested with printing the query:
def product_info(update: Update, context: CallbackContext):
query = update.callback_query.data
print(query)
obj = product.objects.filter(pk=query)
print(obj)
Question: How to reply Django product model object's fields including its image to the user according to the user's chosen product?
Like:
You have chosen a product:
Image of the product
Name of the product
Cost of the product
Here is the answer, I have successed so far:
filename = 'path_until_media_folder' + str(obj.img.url)
update.callback_query.bot.send_photo(update.effective_chat.id, photo=open(filename, 'rb'))

Django RestFramework return just last result after filtered

In my django project i use DjangoRestFramework for expose some API endpoint.
Now i am try to expose just the last result of a model after filtered the data.
Models:
class VarsResults(models.Model):
id = models.AutoField(primary_key=True)
id_res = models.ForeignKey(Results, related_name="mainres", on_delete=models.CASCADE)
var_id = models.ForeignKey(ModbusVariable, null=True, on_delete=models.SET_NULL)
var_val = models.CharField(max_length=400, blank=True)
var_val_conv = models.CharField(max_length=100, blank=True, null=True)
base_byte_order = models.CharField(max_length=15)
var_hash = models.CharField(max_length=400)
serialyzers:
class VarsResultsSerializer(serializers.ModelSerializer):
id_res = ResultsSerializer(read_only=True)
var_id = ModvarSerializer(read_only=True)
class Meta:
model = VarsResults
fields = ['id', 'id_res', 'var_id', 'var_val', 'var_conv', 'var_hash']
views.py
class VarResultsListLast(generics.ListAPIView):
queryset = VarsResults.objects.using(random.choice(replica_list)).order_by('-id')[:1]
serializer_class = VarsResultsSerializer
pagination_class = StandardResultsSetPagination
# paginator = None
filter_backends = [DjangoFilterBackend]
filterset_fields = {
'id_res__id': ['exact'],
'id_res__device': ['exact'],
'id_res__unit__id': ['exact'],
'id_res__proj_code': ['exact'],
'var_id__var_group__short_name': ['exact'],
'id_res__read_date': ['gte', 'lte', 'exact', 'gt', 'lt']
}
search_fields = ['id_res__id', 'id_res__unit__id', 'id_res__device', 'id_res__proj_code', 'id_res__read_date']
ordering_fields = '__all__'
I create my queryset using .order_by('-id')[:1] for have just the last results but it works only if i don't use any filter (return the last result for the whole table), if i try to filter, for example using proj_code or somethin other filter i would to have in return just the last result from the filtered data, but i have:
(call for example: http://127.0.0.1:8000/api/results_data_last/?id_res__proj_code=CR00069)
AssertionError at /api/results_data_last/
Cannot filter a query once a slice has been taken.
How can i execute my filtering options and then have in return just the last record (based on my model id)?
So many thanks in advance
By using .last() you can get the last record. Detailed in the docs
https://docs.djangoproject.com/en/3.2/ref/models/querysets/#last

Get last record by date for multiple table with many-to-one relationship

I have 3 tables :
class Item(models.Model):
def __str__(self):
return self.name
name = models.CharField(max_length=200, unique=True)
image = models.URLField()
class Prix_x1(models.Model):
def __str__(self):
return self.item.name
prix = models.IntegerField(null=True)
saved_at = models.DateTimeField()
item = models.ForeignKey(Item, on_delete=models.CASCADE)
class Prix_x10(models.Model):
def __str__(self):
return self.item.name
prix = models.IntegerField(null=True)
saved_at = models.DateTimeField()
item = models.ForeignKey(Item, on_delete=models.CASCADE)
What i want is to retrive the last price by 1 and by 10 for each Item in my BDD.
I have already tried many things, but either it's not gives me what i want or either the request is like 20seconds
I want to show the result in a table like :
Thanks for your help !
You can work with subqueries:
from django.db.models import OuterRef, Subquery
Item.objects.annotate(
prix_x1=Subquery(
Prix_x1.objects.filter(
item=OuterRef('pk')
).values('prix').order_by('-saved_at')[:1]
),
prix_x10=Subquery(
Prix_x10.objects.filter(
item=OuterRef('pk')
).values('prix').order_by('-saved_at')[:1]
)
)
The Items that arise from this queryset will have two extra attribute .prix_x1 and .prix_x10 that contain the prix for the last Prix_x1 and Prix_x10 records related to that item.

Django Get content from FK of FK

I'm facing troubles with my django project.
I have a three layer model:
model.py
class PLC(models.Model):
name = models.CharField(max_length=50)
class Measure(models.Model):
class Meta:
unique_together = ('PLC', 'address', 'is_bit')
PLC = models.ForeignKey(PLC, on_delete=models.CASCADE)
address = models.IntegerField()
is_bit = models.BooleanField()
class MeasureValue(models.Model):
measure = models.ForeignKey(Measure, on_delete=models.CASCADE)
value = models.TextField()
In my view.py, I want to be able to get measures values out of the plc
class PLCViewSet(viewsets.ModelViewSet):
queryset = PLC.objects.all()
serializer_class = PLCSerializer
def measures(request, id):
plc = PLC.objects.get(id=id)
measures = Measure.objects.filter(PLC=id)
values = MeasureValue.objects.filter(measure__PLC=id)
context = {
'PLC': plc,
'MEASURES': measures,
'VALUES': values
}
return render(request, 'measures.html', context)
Unfortunately, i cannot make a correct filter() (data are stored in sqlite base).
Have you looked into prefetch_related and Prefetch? Those would allow you to pull the related fields when fetching the PLC instance. Then you can access them as follows:
plc = PLC.objects.prefetch_related('measure_set__measurevalue_set').filter(id=id).first()
plc.measure_set.all() # QuerySet of `Measure` that are filtered to the `plc`
for measure in plc.measure_set.all():
measure.measurevalue_set.all() # QuerySet of `MeasureValue` that are filtered to the `measure` therefore `plc`

Access manyTomany field (Label) from category class via subclass(Products) Category-> Products -> Labels

Here is the code of my models file:
from django.db import models
# Create your models here.
class Category(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
class Product(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='products')
name = models.CharField(max_length=255)
price = models.DecimalField(decimal_places=2, max_digits=9)
def __str__(self):
return self.name
class Label(models.Model):
name = models.CharField(max_length=255)
products = models.ManyToManyField(Product, related_name='labels')
def __str__(self):
return self.name
Now I want to access manyTomany field i.e. Label from Category
please help me
Thanks in advance
You can try like this.
# first get the single category object
category = Category.objects.get(pk=1)
# querying products in this category
products = category.products.all()
# now filter the Label model based on the category products
labels = Label.objects.filter(products__in=products)

Resources