Сложный запрос Джанго, сравнивающий 2 модели - PullRequest
0 голосов
/ 29 декабря 2011

Это может быть вопрос о дизайне.

Вопрос: «Каков наилучший способ найти предложения, которые должны иметь обратную связь, отправленную зарегистрированным пользователем».На сайте Отзывы есть 3 вкладки: «Отправлено», «Получено», «Отправить отзыв».На вкладке «Отправить отзыв» есть таблица со ссылкой «Идентификатор предложения», «имя пользователя (покупатель / отправитель» и «Отправить отзыв»), указывающая на форму обратной связи.

Вот код, который должен помочь понять, что я имею в виду.

Предложения отображаются до тех пор, пока какой-либо пользователь не купит их. Предложение закрывается, и для этого предложения создается новый экземпляр Заказа (сохранение деталей заказа).

Я пытаюсь реализовать приложение Feedback,где обе стороны транзакции предложения могут отправить отзыв о транзакции.

Давайте пропустим проблему «завершенного» или «запущенного» предложения.

class Offer(models.Model):
    """Offer is displayed for 5 days, then it's being ended by run everyday cron script.
       If someone buys the offer end_time is being set, and offer is treated as ended.
       Both sides of transaction may send feedback.
    """    
    whose = models.ForeignKey(User, verbose_name="User who has created the offer")
    end_time = models.DateTimeField(blank=True, null=True, help_text="")
    field = ()
    fields2 = ()
    order = models.ForeignKey(Order, balnk=True, null=True, help_text="Order details")

class Order(models.Model):
    """stores order details like user, date, ip etc."""
    order_1_field_details = ()
    who = models.ForeignKey(User, verbose_name="User who bought the offer")
    offer_id = models.PositiveIntegerField("know it's unnecessary")
    offer_data = models.TextField('offer data dict here')

class Feedback(models.Model):
    offer_id = models.PositiveIntegerField()
    sent_by = models.ForeignKey(User, verbose_name="Offer sender")
    received_by = models.ForeignKey(User, verbose_name="Offer receiver")

    def get_offer(self):
        try: 
            Offer.objects.get(id=self.offer_id)
        except Offer.DoesNotExist:
            return None  # offer moved to archive

В первом проекте было предложение = модели.ForeignKey (Предложение) вместо поля offer_id, но я собираюсь переместить некоторые старые предложения из таблицы предложений в другое для архивации. Я хотел бы, чтобы отзывы оставались, даже если я «заархивировал» предложение. В списке отзывов будет «Идентификатор предложения "ссылка и для предложений старше 60 дней пользователь увидит" перенесено в архив "при нажатии" подробности ".

Все, что я могу придумать на данный момент, это сойтиЕсли срок годности не истек, но был покупатель.

окончен () - менеджер, вернувший self.filter(end_date__isnull=False)

offers_with_buyer = models.Q(Offer.objects.ended().filter(whose__exact=request.user, order__isnull=False) | models.Q(Offer.objects.ended().filter(order__who__exact=request.user)

Как проверить, есть ли отзывы об этих предложениях?Я знаю, что должен вернуть пользователя и предложить идентификатор из набора запросов выше и проверить, существуют ли они в Feedback.offer_id и Feedback.sent_by .. или, возможно, мне следует полностью изменить дизайн модели ...

1 Ответ

1 голос
/ 29 декабря 2011

Во-первых, то, как вы справляетесь с датой окончания, очень надумано. Если предложение заканчивается через 5 дней после его создания, просто установите его автоматически:

from datetime import datetime, timedelta

class Offer(models.Model):
     ...
     def save(self, *args, **kwargs):
         self.end_date = datetime.now() + timedelta(days=5)

         super(Offer, self).save(*args, **kwargs)

Затем просто измените ваш ended менеджер, чтобы он возвращался: self.filter(end_date__lte=datetime.now())

Однако я обычно предпочитаю добавлять дополнительные методы в менеджер по умолчанию для обработки данных различными способами:

class OfferQuerySet(models.query.QuerySet):
    def live(self):
        return self.filter(end_date__gt=datetime.now())

    def ended(self):
        return self.filter(end_date__lte=datetime.now())

class OfferManager(models.Manager):
    use_for_related_fields = True

    def get_query_set(self):
        return OffersQuerySet(self.model)

    def live(self, *args, **kwargs):
        return self.get_query_set().live(*args, **kwargs)

    def ended(self, *args, **kwargs):
        return self.get_query_set().ended(*args, **kwargs)

(Определение пользовательского QuerySet и последующее использование методов на Manager, которые просто являются прокси для QuerySet, - это способ, которым основная команда Django делает это, и позволяет вам использовать методы в любом месте цепь, а не только передняя.)

Что касается "архивирования" вашего Offers, то крайне плохо разделять похожие данные на две разные модели / таблицы. Это экспоненциально увеличивает порядок сложности в вашем приложении. Если вы хотите «заархивировать» предложение. Добавьте поле, например:

 is_archived = models.BooleanField(default=False)

Затем вы можете создать другой метод в ваших подклассах Manager и QuerySet, чтобы отфильтровать только заархивированные или действующие предложения:

 def live(self):
     return self.filter(is_archived=False, end_date__gt=datetime.now())

 def archived(self):
     return self.filter(is_archived=True)

Если вам действительно нужна другая модель (например, для отдельного представления в администраторе только что заархивированных предложений), вы можете создать модель прокси:

 class ArchivedOfferManager(models.Manager):
     def get_query_set(self):
         return super(ArchivedOfferManager, self).get_query_set().filter(is_archived=True)

     def create(self, **kwargs):
         kwargs['is_archived'] = True
         return super(ArchivedOfferManager, self).create(**kwargs)

 class ArchivedOffer(models.Model)
     class Meta:
         proxy = True

     objects = ArchivedOfferManager()

     def save(self, *args, **kwargs):
         self.is_archived = True
         super(ArchivedOffer, self).save(*args, **kwargs)
...