Django queryset - column IN() GROUP BY HAVING COUNT DISTINCT - django

With the following models:
class Post(models.Model):
class Meta:
db_table = "posts"
class Tag(models.Model):
tag = models.CharField(max_length=50)
class Meta:
db_table = "tags"
class PostTag(models.Model):
postid = models.PositiveIntegerField()
tagid = models.PositiveIntegerField()
class Meta:
unique_together = ("postid", "tagid")
db_table = "posttags"
To get postids of posts which contain all the tagids given in TAGLIST where TAGLEN is the number of tagids in TAGLIST:
SELECT postid
FROM posttags
WHERE tagid IN (TAGLIST)
GROUP BY postid
HAVING COUNT(DISTINCT tagid) = TAGLEN
But how do I do this with Django ORM?

I found a solution.
TAGLEN = TAGLIST.count()
withtags = PostTag.objects.filter(tagid__in=TAGLIST)
withall = withtags.values("postid").annotate(tagtotal=Count("tagid", distinct=True)).order_by()
withall.filter(tagtotal=TAGLEN).values_list("postid", flat=True)
And running .query.__str__() on all this returns basically the following SQL below.
SELECT "postid"
FROM "posttags"
WHERE "tagid" IN (TAGLIST)
GROUP BY "postid"
HAVING COUNT(DISTINCT "tagid") = TAGLEN'

Related

Django admin site: how to change admin URL based on verbose_name

class Meta: verbose_name only changes its displayed name. But when its clicked. i want its url changed too based on verbose name
Here's my code:
Models:
tables = [f.id for f in StockId.objects.raw(
"SELECT TABLE_NAME AS id FROM INFORMATION_SCHEMA.tables WHERE TABLE_NAME LIKE 'my_project_auth_stockid%%'")]
table_map = {}
for tbl in tables:
class Stocks(models.Model):
user_id = models.IntegerField()
product_name = models.CharField(max_length=100)
mrp = models.IntegerField()
selling_price = models.IntegerField()
class Meta:
db_table = tbl
managed = False
verbose_name = _(tbl)
verbose_name_plural = _(tbl)
table_map[tbl] = Stocks
Admin.py
from my_project_auth.models import *
class AllStocksAdmin(AdminSite):
site_header = 'All User Stocks'
stockadmin = AllStocksAdmin(name="stockadmin")
for tbl in tables:
class StocksAdmin(ImportExportModelAdmin):
resource_class = StocksAdminResource
list_display = ["product_name"]
stockadmin.register(table_map[tbl], StocksAdmin)
What I am trying to do is in above to make multiple stock table out of single model. But Admin panel for each stocks is not working. All stocks table is pointing towards single table because of admin URL is based on model's class name.
I'm trying to do something like this in Admin.py, please suggest changes:
for tbl in tables:
class StocksAdmin(ImportExportModelAdmin):
resource_class = StocksAdminResource
list_display = ["product_name"]
def get_urls(self): # Not Working
urls = super(StocksAdmin, self).get_urls()
my_urls = path(f'stockadmin/{tbl}/', self.admin_view(stockadmin))
return my_urls + urls
To solve the problem of having multiple models with the same name we can generate then dynamically with unique names using the three argument form of type
from django.db import models
tables = [f.id for f in StockId.objects.raw(
"SELECT TABLE_NAME AS id FROM INFORMATION_SCHEMA.tables WHERE TABLE_NAME LIKE 'my_project_auth_stockid%%'")]
class StockBase(models.Model):
user_id = models.IntegerField()
product_name = models.CharField(max_length=100)
mrp = models.IntegerField()
selling_price = models.IntegerField()
class Meta:
abstract = True
for tbl in tables:
class Meta:
db_table = tbl
managed = False
verbose_name = tbl
verbose_name_plural = tbl
locals()[f'Stocks{tbl}'] = type(f'Stocks{tbl}', (StockBase,), {'Meta': Meta, '__module__': StockBase.__module__})
This will result in multiple models with the same fields, from the base class, and all with unique names.
I used __subclasses__ in the admin to get all the created models but it's pretty much the same, just register all the models.
from django.contrib import admin
from . import models
class StocksAdmin(admin.ModelAdmin):
list_display = ["product_name"]
for cls in models.StockBase.__subclasses__():
admin.site.register(cls, StocksAdmin)
Now your admin should be full of tens/hundreds/thousands of duplicate but named slightly different models

How to get data from two tables with DjangoORM?

I have two tables in the database:
class CustomerEquipment(models.Model):
serial_number = models.CharField(max_length=255)
state_timestamp = models.DateTimeField()
state_type = models.IntegerField()
class Meta:
managed = False
db_table = 'customer_equipment'
class LogCustomerEquipment(models.Model):
state = models.IntegerField()
state_timestamp = models.DateTimeField()
serial_number = models.CharField(max_length=255)
class Meta:
managed = False
db_table = 'log_customer_equipment'
I execute two database queries:
customer_equipment_list = CustomerEquipment.objects.using('portal').filter(
Q(state_type=10) & Q(state_timestamp__icontains='2020-02-27')
)
log_customer_equipment_list = LogCustomerEquipment.objects.using('portal').filter(
Q(state=2) & Q(state_timestamp__icontains='2020-02-27')
)
I need to get serial_number which are in both tables.
How to do it? How can I optimize queries? The same serial_number can be in both tables. Need to choose those that intersect.
You can try this:
set(CustomerEquipment.objects.values_list('serial_number ', flat=True)) & set(LogCustomerEquipment.objects.values_list('serial_number ', flat=True))
or maybe you should refactor models from the relation.
For example, using ForeignKey or ManyToManyField.

select related in Django

I am trying to accomplish something very similar to:
How to join 3 tables in query with Django
Essentially, I have 3 tables. In the Django REST we are showing table 3. As you see below (models.py), table 3 has company_name which is a foreign key of table 2 and table 2 is a foreign key of table 1. Both table 2 and 3 are linked by the table 1 ID. Table 1 contains the actual text, which we want to display in the API output, not the ID number.
Table 1: Manufacturer of Car -- Table 2: What the Car is -- Table 3: list of all cars
Models.py
Table 1:
class ManufacturerName(models.Model):
name_id = models.AutoField(primary_key=True)
company_name = models.CharField(unique=True, max_length=50)
class Meta:
managed = False
db_table = 'manufacturer_name'
Table 2:
class CarBuild(models.Model):
car_id = models.AutoField(primary_key=True)
car_icon = models.CharField(max_length=150, blank=True, null=True)
company_name = models.ForeignKey('ManufacturerName', models.DO_NOTHING, db_column='ManufacturerName')
class Meta:
managed = False
db_table = 'car_build'
Table 3:
class CarList(models.Model):
list_id = models.AutoField(primary_key=True)
company_name = models.ForeignKey('CarBuild', models.DO_NOTHING, db_column='CarBuild')
title = models.CharField(unique=True, max_length=100)
description = models.TextField()
class Meta:
managed = False
db_table = 'cars'
Within my views:
This is what I am trying, based on the foreign key relationships:
queryset = CarList.objects.all().select_related('company_name__company_name')
I get no errors when I save and run this, however, the ID is still being returned, and not the text associated with the foreign key relationships:
[
{
"list_id": 1,
"company_name": "http://127.0.0.1:8000/api/1/",
"title": "Really fast car you're driving, and this is dummy text",
Again, I would like to achieve getting the text associated with the company_name foreign key relationships from table 1 to show in the JSON.
serializer and viewset
class manufacturer_name(serializers.HyperlinkedModelSerializer):
class Meta:
model = manufacturer_name
fields = ('name_id', 'company_name')
class manufacturer_name(viewsets.ModelViewSet):
queryset = manufacturer_namee.objects.all()
serializer_class = manufacturer_name
class CarBuildViewSet(viewsets.ModelViewSet):
queryset = CarBuild.objects.all()
serializer_class = CarBuildSerialiser
class CarBuildSerialiser(serializers.HyperlinkedModelSerializer):
class Meta:
model = CarBuild
fields = ('car_id', 'car_icon', 'company_name')
class CarListSerialiser(serializers.HyperlinkedModelSerializer):
class Meta:
model = News
fields = ('list_id', 'company_name', 'title')
class CarListViewSet(viewsets.ModelViewSet):
serializer_class = CarList
def get_queryset(self):
queryset = News.objects.all().select_related('company_name__company_name')
return queryset
Based on detailed conversation to clear few details. Here is the answer.
You need to make small changes to your models as it was quite confusing to understand what you want to achieve.
Models:
class ManufacturerName(models.Model):
name_id = models.AutoField(primary_key=True)
company_name = models.CharField(unique=True, max_length=50)
class Meta:
managed = False
db_table = 'manufacturer_name'
class CarBuild(models.Model):
car_id = models.AutoField(primary_key=True)
car_icon = models.CharField(max_length=150, blank=True, null=True)
manufacturer = models.ForeignKey(ManufacturerName,on_delete=models.SET_NULL)
class Meta:
managed = False
db_table = 'car_build'
class CarList(models.Model):
list_id = models.AutoField(primary_key=True)
car = models.ForeignKey(CarBuild, on_delete=models.DO_NOTHING)
title = models.CharField(unique=True, max_length=100)
description = models.TextField()
class Meta:
managed = False
db_table = 'cars'
And then You need to adjust your serializers.
class CarListSerialiser(serializers.HyperlinkedModelSerializer):
company_name= serializers.SerializerMethodField(read_only=True)
class Meta:
model = CarList
fields = ('list_id', 'company_name', 'title')
def get_company_name(self, obj):
return obj.car.manufacturer.company_name
And you use it in your view:
class CarListViewSet(viewsets.ModelViewSet):
queryset = CarList.object.all()
serializer_class = CarListSerialiser

Nested serializer with Django-Rest-Framework

I've been trying to use a nested serializer with DRF but it won't display the related item in the output.
Here's my model.py :
class Categorie(models.Model):
nom = models.CharField(max_length=100)
def __unicode__(self):
return unicode(self.nom)
class Item(models.Model):
nom = models.CharField(max_length=100)
disponible_a_la_vente = models.BooleanField(default = True)
nombre = models.IntegerField()
prix = models.DecimalField(max_digits=5, decimal_places=2)
history = HistoricalRecords()
categorie = models.ForeignKey(Categorie, models.CASCADE)
class Meta:
verbose_name = "item"
verbose_name_plural = u"inventaire"
ordering = ['categorie', 'nom']
def __unicode__(self):
return u'{nom} - {nombre}'.format(nom = self.nom, nombre = self.nombre)
and my serializers.py
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = ('nom',)
class CategorieSerializer(serializers.ModelSerializer):
items = ItemSerializer(many=True)
class Meta:
model = Categorie
fields = ('nom', 'id', 'items')
The view i'm currently testing is very basic :
class InventaireDetail(generics.RetrieveAPIView):
queryset = Categorie.objects.all()
serializer_class = CategorieSerializer
but it gives the error:
AttributeError: Got AttributeError when attempting to get a value for
field items on serializer CategorieSerializer. The serializer
field might be named incorrectly and not match any attribute or key on
the Categorie instance. Original exception text was: 'Categorie'
object has no attribute 'items'.
I've been looking for a while now but i can't get it working even with the help of the doc.
Categorie.items does not exist. By default the reverse relation would get the name Categorie.item_set. You can fix that in two ways.
EITHER: add related_name to your foreign key.
class Item(models.Model):
categorie = models.ForeignKey(Categorie, models.CASCADE, related_name='items')
OR: another solution is to change the CategorieSerializer
class CategorieSerializer(serializers.ModelSerializer):
items = ItemSerializer(many = True, read_only=True, source='item_set')

Django how to deal with foreign keys in child tables

I am new in DJango. I have a database with three tables:
> -CRP
> - SubComponents of a CRP
> - Objectives of a SubComponent of a CRP.
I have the following models:
class period(models.Model):
period_code = models.IntegerField(primary_key=True,verbose_name='Period code')
period_desc = models.CharField(max_length=45,verbose_name='Period description')
period_current = models.IntegerField(verbose_name='Current period')
def __unicode__(self):
return self.period_desc
class Meta:
db_table = 'period'
class crp(models.Model):
crp_code = models.CharField(max_length=3,primary_key=True,verbose_name='CRP code')
crp_desc = models.CharField(max_length=45,verbose_name='CRP description')
def __unicode__(self):
return self.crp_desc
class Meta:
db_table = 'crp'
class subcomponent(models.Model):
crp_code = models.ForeignKey(crp,db_column='crp_code',related_name='subcomponent_crp_code',to_field='crp_code',primary_key=True,verbose_name='CRP code')
subc_code = models.CharField(max_length=3,primary_key=True,verbose_name='SubComponent Code')
subc_desc = models.CharField(max_length=45,verbose_name='SubComponent Description')
def __unicode__(self):
return self.subc_desc
class Meta:
db_table = 'subcomponent'
class objective(models.Model):
crp_code = models.ForeignKey(subcomponent,db_column='crp_code',related_name='objective_crp_code',to_field='crp_code',primary_key=True,verbose_name='CRP code')
subc_code = models.ForeignKey(subcomponent,db_column='subc_code',related_name='objective_subc_code',to_field='subc_code',primary_key=True,verbose_name='SubComponent Code')
obj_year = models.ForeignKey(period,db_column='obj_year',related_name='objective_obj_year',to_field='period_code',primary_key=True,verbose_name='Objective year')
obj_code = models.CharField(max_length=7,primary_key=True,verbose_name='Objective code')
obj_desc = models.CharField(max_length=45,verbose_name='Objective description')
def __unicode__(self):
return self.obj_desc
class Meta:
db_table = 'objective'
All works fine for the "subcomponent" model (reference to CRP); in the administration of "subcomponent" the user can pull down a CRP and add a subcomponent to it.
However, the model "objective" reference the model "subcomponent". In the administration of "objective" I would like the user to pull down and select a CRP, then filter the subcomponents of that CRP also in a pull down list. How can I do this?
Many thanks,
Carlos.
Take a look on django-smart-selects