Необработанный запрос Django дает одинаковый результат на всех моделях - PullRequest
0 голосов
/ 01 июня 2019

У меня 3 модели Product, Photo, and ProductLikeDilike.Я выполняю левое внешнее соединение на всех 3 моделях.Сначала я присоединяюсь к Product с Photo, а затем к таблице результатов (temp) я присоединяюсь к ProductLikeDilike.Ниже приведен необработанный sql.

Примечание : olx - это имя приложения django.

data = Product.objects.raw('select * from (select 
    olx_product.id,olx_product.name,olx_photo.file,olx_photo.cover_photo_flag 
    from olx_product left outer join olx_photo on 
    (olx_product.id=olx_photo.reference_id_id) where 
    olx_photo.cover_photo_flag="yes" or olx_photo.cover_photo_flag is null) as 
    temp left outer join olx_productlikedislike on 
    (temp.id=olx_productlikedislike.product_id_id and 
    olx_productlikedislike.product_liked_by_id_id=2)')

for x in data:
  print(x.name)

Что я хочу понять, когда использую любой извышеупомянутые 3 модели, чтобы запустить сырой sql, почему я получаю тот же результат, то есть

Когда я делаю

data = Product.objects.raw('select *.....')
for x in data:
  print(x.name)

или

data = Photo.objects.raw('select *......') 
for x in data:
  print(x.name)

или

data = ProductLikeDislike.raw('select *.....')
for x in data:
  print(x.name)

Я получаю тот же результат.Зачем?Пожалуйста, помогите мне понять это.

Ниже приведен файл models.py

from django.db import models
from django.urls import reverse
from django.dispatch import receiver
from django.contrib.auth.models import User



    class Product(models.Model):
        category = models.ForeignKey(Category ,on_delete=models.CASCADE)
        name = models.CharField(max_length = 200, db_index = True)
        slug = models.SlugField(max_length = 200, db_index = True)     
        description = models.TextField(blank = True)    
        price = models.DecimalField(max_digits = 10, decimal_places = 2 )#Not used FloatField to avoid rounding issues
        created = models.DateTimeField(auto_now_add=True)
        updated = models.DateTimeField(auto_now=True)

        contact= models.BigIntegerField(default=None,blank=True, null=True)
        created_by = models.CharField(max_length = 200, default=None,blank=True, null=True)
        uploaded_by_id = models.IntegerField(default=0)
        status = models.IntegerField(default=0) # 0-->Active,1-->Inactive
        mark_as_sold = models.IntegerField(default=0) # 0-->not sold,1-->sold

        def get_absolute_url(self):
            return reverse('olx:edit_product', kwargs={'pk': self.pk})



        class Meta:
            ordering = ('-created',)
            index_together = (('id','slug'),)# we want to query product by id and slug using together index to improve performance

        def __str__(self):
            return self.name


    class Photo(models.Model):

        reference_id = models.ForeignKey(Product, null=True,on_delete=models.CASCADE) 
        photo_type = models.CharField(max_length = 70, db_index = True)
        file = models.FileField(upload_to='photos/',default='NoImage.jpg')
        cover_photo_flag = models.CharField(default=0,max_length = 5, db_index = True)
        uploaded_at = models.DateTimeField(auto_now_add=True)
        uploaded_by_id = models.IntegerField(default=0)
        status = models.IntegerField(default=0) # 0-->Active,1-->Inactive



        class Meta:
            ordering = ('-uploaded_at',)


    class ProductLikeDislike(models.Model):
        product_id = models.ForeignKey(Product,models.SET_DEFAULT,default=0) 
        product_liked_by_id = models.ForeignKey(User,models.SET_DEFAULT,default=0) 
        status = models.BooleanField(default=False)

И, пожалуйста, также покажите мне, как написать его чистым способом Django, если это возможно?

Ответы [ 2 ]

1 голос
/ 01 июня 2019

Я получаю тот же результат.Зачем?Пожалуйста, помогите мне понять это.

Потому что .raw(..) [Django-doc] просто принимает необработанный запрос и выполняет его.Модель, из которой выполняется raw, не имеет значения.

Мы можем сгенерировать запрос, который выглядит следующим образом:

from django.db.models import <b>Q</b>

Product.objects.filter(
    Q(photo__photo_flag__isnull=True) | Q(photo__photo_flag='yes'),
    Q(likedislike__product_liked_by_id_id=2)
)

Так что здесь мы принимаем все Product s, для которых связан Photo объект имеет flag, равное NULL (это также происходит в случае, если JOIN не выдает никаких флагов), или photo_flag равно 'yes').Кроме того, должен быть объект Likedislike, где liked_by_id_id равен 2.

Обратите внимание, что обычно ForeignKey [Django-doc] имеет нет _id суффикс или id_ префикс.Также немного странно, что вы устанавливаете default=0 для этого, тем более что большинство баз данных присваивают только строго положительных значений в качестве первичных ключей, и нет смысла изначально присваивать 0 другимобъект в любом случае.

1 голос
/ 01 июня 2019

Примерно так:

    user_i_care_about = User.objects.get(username='user2')
    productlikedislike_set = models.Prefetch('productlikedislike_set',
                                             ProductLikeDislike.objects.select_related('product_liked_by') \
                                                               .filter(product_liked_by=user_i_care_about) \
                                                               .order_by('id'))
    photo_set = models.Prefetch('photo_set', Photo.objects.all())  # this is here incase you need to a select_related()

    products = Product.objects.prefetch_related(photo_set, productlikedislike_set) \
                              .filter(models.Q(photo__cover_photo_flag='yes') | models.Q(photo__isnull=True)) \
                              .filter(productlikedislike__product_liked_by=user_i_care_about)

Тогда вы можете использовать:

for product in products:
    for pic in product.photo_set.all():
        print(x.file.name)
    # every product here WILL be liked by the user

если ваши модели выглядят примерно так:

 class Product(models.Model):
    # category = models.ForeignKey(Category, on_delete=models.CASCADE)  # TODO: uncomment, didnt want to model this out
    name = models.CharField(max_length=200, db_index=True)
    slug = models.SlugField(max_length=200, db_index=True)
    description = models.TextField(blank=True)
    price = models.DecimalField(max_digits=10, decimal_places=2)  # Not used FloatField to avoid rounding issues  # this is correct, no need to explain this, anyonw that works with django, gets this.
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    contact = models.BigIntegerField(default=None,blank=True, null=True)
    created_by = models.CharField(max_length=200, default=None, blank=True, null=True)
    uploaded_by_id = models.IntegerField(default=0)  # TODO: use ForeignKey(User) here!!!
    status = models.IntegerField(default=0)  # 0-->Active,1-->Inactive  # TODO: learn to use `choices`
    mark_as_sold = models.IntegerField(default=0)  # 0-->not sold,1-->sold  # TODO: there is something called `BooleanField` use it!

    class Meta:
        ordering = ('-created',)
        index_together = (('id', 'slug'),)  # we want to query product by id and slug using together index to improve performance

    def get_absolute_url(self):
        return reverse('olx:edit_product', kwargs={'pk': self.pk})

    def __str__(self):
        return self.name


class Photo(models.Model):

    product = models.ForeignKey(Product, null=True,on_delete=models.CASCADE, db_column='reference_id')
    photo_type = models.CharField(max_length=70, db_index=True)
    file = models.FileField(upload_to='photos/', default='NoImage.jpg')
    cover_photo_flag = models.CharField(default=0, max_length=5, db_index=True)  # TODO: learn to use `choices`, and you use "yes" / "no" -- and the default is 0 -- FIX THIS!!
    uploaded_at = models.DateTimeField(auto_now_add=True)
    uploaded_by_id = models.IntegerField(default=0)  # TODO: use ForeignKey(User) here!!!
    status = models.IntegerField(default=0)  # 0-->Active,1-->Inactive  # TODO: learn to use `choices` -- perhaps just call this "is_active" and make it a bool

    class Meta:
        ordering = ('-uploaded_at',)


class ProductLikeDislike(models.Model):
    product = models.ForeignKey(Product, models.SET_DEFAULT, default=0)  # TODO: default=0?? this is pretty bad. models.ForeignKey(Product, models.SET_NULL, null=True) is much better
    product_liked_by = models.ForeignKey(User, models.SET_DEFAULT, default=0, db_column='product_liked_by_id')  # TODO: default=0?? this is pretty bad. models.ForeignKey(ForeignKey, models.SET_NULL, null=True) is much better
    status = models.BooleanField(default=False)  # TODO: rename, bad name. try something like "liked" / "disliked" OR go with IntegerField(choices=((0, 'Liked'), (1, 'Disliked')) if you have more than 2 values.

Полный пример тестов WITH можно посмотреть здесь: https://gist.github.com/kingbuzzman/05ed095d8f48c3904e217e56235af54a

...