Как выполнить этот sql в модели Django? - PullRequest
2 голосов
/ 29 сентября 2010
SELECT *, SUM( cardtype.price - cardtype.cost ) AS profit  
FROM user  
LEFT OUTER JOIN  card ON (  user.id =  card.buyer_id )  
LEFT OUTER JOIN  cardtype ON (  card.cardtype_id =  cardtype.id )  
GROUP BY user.id  
ORDER BY profit DESC

Я пробовал это:

User.objects.extra(select=dict(profit='SUM(cardtype.price-cardtype.cost)')).annotate(sum=Sum('card__cardtype__price')).order_by('-profit')

Но Django автоматически добавил SUM( cardtype.price ) к предложению GROUP BY, и SQL не запускается .

Можно ли это сделать без необработанных SQL-запросов?


Укажите модель, не берите в голову эти китайские иероглифы:)

class User(models.Model):

    class Meta:
        verbose_name = "用户"
        verbose_name_plural = "用户"
        ordering = ['-regtime']

    user_status= (
        ("normal", "正常"),
        ("deregistered", "注销"),
        ("locked", "锁定"),
    )

    name = models.CharField("姓名", max_length=20, db_index=True)
    spec_class = models.ForeignKey(SpecClass, verbose_name="专业班级")
    idcard = models.CharField("身份证号", max_length=18)
    mobileno = models.CharField("手机号", max_length=11)
    password = models.CharField("密码", max_length=50) # plain
    address = models.CharField("住址", max_length=100)
    comment = models.TextField("备注")
    certserial = models.CharField("客户证书序列号", max_length=100)
    regtime = models.DateTimeField("注册时间", default=datetime.datetime.now)
    lastpaytime = models.DateTimeField("上次付款时间", default=datetime.datetime.now)
    credit = models.FloatField("信用额度", default=100)
    money = models.FloatField("余额", default=0)
    use_password = models.BooleanField("使用密码")
    use_fetion = models.BooleanField("接收飞信提示")
    status = models.CharField("账户状态", choices = user_status, default="normal", max_length=20, db_index=True)

    def __unicode__(self):
        return self.name

class CardType(models.Model):

    class Meta:
        verbose_name = "点卡类型"
        verbose_name_plural = "点卡类型"
        ordering = ['name']

    name = models.CharField("类型名称", max_length=20, db_index=True)
    note = models.CharField("说明", max_length=100)
    offcial = models.BooleanField("官方卡", default=True)
    available = models.BooleanField("可用", default=True, db_index=True)
    payurl = models.CharField("充值地址", max_length=200)
    price = models.FloatField("价格")
    cost = models.FloatField("进货价格")

    def __unicode__(self):
        return u"%s(%.2f元%s)" % (self.name, self.price, u", 平台卡" if not self.offcial else "")

    def profit(self):
        return self.price - self.cost
    profit.short_description = "利润"


class Card(models.Model):

    class Meta:
        verbose_name = "点卡"
        verbose_name_plural = "点卡"
        ordering = ['-createtime']

    card_status = (
        ("instock", "未上架"),
        ("available", "可用"),
        ("sold", "已购买"),
        ("invalid", "作废"),
        ("returned", "退卡"), # sell to the same person !
        ("reselled", "退卡重新售出"),
    )

    cardtype = models.ForeignKey(CardType, verbose_name="点卡类型")
    serial = models.CharField("卡号", max_length=40)
    password = models.CharField("卡密", max_length=20)
    status = models.CharField("状态", choices = card_status, default="instock", max_length=20, db_index=True)
    createtime = models.DateTimeField("入库时间")
    buytime = models.DateTimeField("购买时间", blank=True, null=True)
    buyer = models.ForeignKey(User, blank=True, null=True, verbose_name="买家")

    def __unicode__(self):
        return u'%s[%s]' % (self.cardtype.name, self.serial)

Ответы [ 2 ]

1 голос
/ 29 сентября 2010

Во-первых, одно из внешних объединений кажется плохой идеей для такого рода вещей. Поскольку вы предоставили нет информации о вашей модели, я могу только догадываться.

Вы говорите, что у вас может не быть КАРТОЧКИ для каждого пользователя? Это имеет некоторый смысл.

Вы также говорите, что у некоторых карт нет типов карт? Это не часто имеет смысл. Вы не предоставили никаких деталей. Однако, если у Карты нет Типа карты, могу поспорить, у вас либо проблемы в другом месте вашего приложения, либо вы выбрали действительно плохие имена, которые не дают наименьшего представления о том, что означают эти вещи. Вы должны исправить другие части вашего приложения, чтобы убедиться, что у каждой карты действительно есть тип карты. Или вы должны исправить свои имена, чтобы иметь смысл.

Очевидно, что оператор ORM использует внутренние объединения, а ваш SQL использует внешние объединения. Какой реальный вопрос? Как правильно делать внешние соединения? Если вы потратите время на поиск [Django] и Left Outer Join, вы увидите, что Raw SQL - ужасная идея.

Или реальный вопрос, как правильно сделать сумму? Из вашего собственного ответа видно, что SQL неверен, и у вас действительно возникают проблемы с суммой. Если это так, пожалуйста, очистите SQL, чтобы он был правильным.

Если внешние объединения являются частью проблемы, а не только визуальным шумом, то вы должны сделать что-то подобное для внешнего объединения с суммой.

def user_profit():
    for u in User.objects.all():
        profit = sum[ t.price - t.cost
            for c in u.card_set.all()
                for t in c.cardtype_set.all() ]
        yield user, profit

В функции просмотра вы можете указать значение функции для шаблона для отображения отчета. Поскольку это генератор, огромный список не создается в памяти. Если вам нужно выполнить нумерацию страниц, вы можете предоставить генератор для нумератора страниц, и все работает достаточно хорошо.

Это часто сопоставимая скорость со сложным необработанным SQL-запросом с большим количеством внешних объединений.

Если, действительно, отношения типа карта-карта на самом деле не являются обязательными, то вы можете немного сократить это. Вам еще нужно подумать о внешнем соединении.

def user_profit():
    for u in User.objects.all():
        profit = sum[ c.cardtype.price - c.cardtype.cost
            for c in u.card_set.all() ]
        yield user, profit
0 голосов
/ 29 сентября 2010

Ну, я нашел этот
Сумма вычисляемого столбца в Django QuerySet

Пришлось использовать сырой SQL сейчас ...
Спасибо вам обоим!

...