Система проверки библиотек на основе Django - PullRequest
1 голос
/ 18 ноября 2011

Я работаю над библиотечной системой для управления определенными элементами в нашем офисе, мне не нужна полноценная интегрированная библиотечная система, поэтому я решил сделать одну ручную работу с Django.

Ниже приведен упрощенныйверсия моей модели:

class ItemObjects(models.Model):

# Static Variables
IN_STATUS        = 'Available'
OUT_STATUS       = 'Checked out'
MISSING          = 'Missing'
STATUS_CHOICES   = (
    (IN_STATUS,  'Available'),
    (OUT_STATUS, 'Checked out'),
    (MISSING,    'Missing'),
)

# Fields
slug             = models.SlugField(unique=True)
date_added       = models.DateField(auto_now_add=True)
last_checkin     = models.DateTimeField(editable=False, null=True)
last_checkout    = models.DateTimeField(editable=False, null=True)
last_activity    = models.DateTimeField(editable=False, null=True)
status           = models.CharField(choices=STATUS_CHOICES, default=IN_STATUS, max_length=25)
who_has          = models.OneToOneField(User, blank=True, null=True)
times_out        = models.PositiveIntegerField(default=0, editable=False)
notes            = models.CharField(blank=True, max_length=500)
history          = models.TextField(blank=True, editable=False)
pending_checkin  = models.BooleanField(default=False)
pending_transfer = models.BooleanField(default=False)

Сначала я использовал метод на ItemObject для обработки извлечения элемента у пользователя, а who_has был EmailField, потому что я не мог получитьCharfField для заполнения именем пользователя, вошедшего в систему, но я решил, что использование OneToOneField, вероятно, ближе к «правильному» способу сделать это. Хотя who_has был EmailField, работал следующий метод:

    def check_out_itemobject(self, user):
        user_profile                     = user.get_profile()
        if self.status == 'Available' and self.who_has == '':
            self.status                  = 'Checked out'
            self.who_has                 = user.email
            self.last_checkout           = datetime.datetime.now()
            self.last_activity           = datetime.datetime.now()
            self.times_out               += 1
            if self.history == '':
                self.history             += "%s" % user_profile.full_name
            else:
                self.history             += ", %s" % user_profile.full_name
            if user_profile.history == '':
                user_profile.history     += self.title
            else:
                user_profile.history     += ", %s" % self.title
        else:
            return False # Not sure is this is "right"
        user_profile.save()
        super(ItemObjects, self).save()

Теперь, когда я использую OneToOneField, это не работает, поэтому я начал смотреть на использование подкласса ModelForm, но ни один из случаев, которые я видел здесь на SO, казалось, не подходит дляЯ пытаюсь сделать;моя форма была бы кнопкой, и все тут.Вот некоторые из вопросов, на которые я смотрел:

Django: сохранение нескольких моделей одновременно (сложный случай)

(Django) (проблемы с внешними ключами).person_id Не может быть NULL

Обновление модели django

Так что я был на правильном пути с каким-то измененным методом save () илиПодкласс ModelForm будет подходить?

РЕДАКТИРОВАТЬ / ОБНОВИТЬ: Большое спасибо @ChrisPratt!

Так что я пытаюсь заставить предложение Криса Пратта показать ItemHistory работать, но когда я пытаюсьвизуализировать его на странице, я получаю AttributeError, который утверждает, что «объект« Пользователь »не имеет атрибута« отметка времени »».Поэтому мой вопрос: почему он жалуется на объект User, когда last_activity является атрибутом объекта ItemObject?

Мой взгляд:

@login_required
def item_detail(request, slug):
    item      = get_object_or_404(Item, slug=slug)
    i_history = item.last_activity
    user      = request.user

    return render_to_response('items/item_detail.html',
                              { 'item'     : item,
                                'i_history': i_history,
                                'user'     : user })

Я непонять, почему в этот момент появляется объект User.

EDIT2: не имеет значения, история - это явно поле M2M, целью которого является пользователь.Вот почему!

1 Ответ

3 голосов
/ 18 ноября 2011

Предполагая, что пользователи будут входить в систему и извлекать книги для себя, то, скорее всего, вы хотите от ForeignKey до User.Книга будет иметь только один User в любой момент времени, но предположительно User s может проверить и другие предметы.Если есть какое-то ограничение, даже если оно на самом деле составляет один на пользователя, было бы лучше проверить это в методе clean модели.Что-то вроде:

def clean(self):
    if self.who_has and self.who_has.itemobject_set.count() >= LIMIT:
        raise ValidationError('You have already checked out your maximum amount of items.')

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

class ItemObject(models.Model):
    AVAILABLE = 1
    CHECKED_OUT = 2
    STATUS_CHOICES = (
        (AVAILABLE, 'Available'),
        (CHECKED_OUT, 'Checked Out'),
    )

    ...

    status = models.PositiveIntegerField(choices=STATUS_CHOICES, default=AVAILABLE)

Затем вы можете запускать свои проверки следующим образом:

if self.status == self.STATUS_AVAILABLE:
    self.status = self.STATUS_CHECKED_OUT

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

Далее history должно быть ManyToManyField.Прямо сейчас, ваша «история» - это только кто последний извлек элемент или какой последний элемент был извлечен пользователем, и в результате он довольно бесполезен.

class ItemObject(models.Model):
    ...
    history = models.ManyToManyField(User, through='ItemHistory', related_name='item_history', blank=True)

class ItemHistory(models.Model):
    CHECKED_OUT = 1
    RETURNED = 2
    ACTIVITY_CHOICES = (
        (CHECKED_OUT, 'Checked Out'),
        (RETURNED, 'Returned'),
    )

    item = models.ForeignKey(ItemObject)
    user = models.ForeignKey(User)
    activity = models.PostiveIntegerField(choices=ACTIVITY_CHOICES)
    timestamp = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-timestamp'] # latest first

Что затем позволяет вам получить полный объемhistory:

some_item.history.all()
some_user.item_history.all()

Чтобы добавить новую историю, вы должны сделать:

ItemHistory.objects.create(item=some_item, user=some_user, activity=ItemHistory.CHECKED_OUT)

Атрибут auto_now_add гарантирует, что отметка времени будет автоматически установлена ​​при создании отношения.

Затем вы могли бы полностью избавиться от полей last_checkout и last_activity и использовать что-то вроде следующего:

class ItemObject(models.Model):
    ...
    def _last_checkout(self):
        try:
            return self.history.filter(activity=ItemHistory.CHECKED_OUT)[0].timestamp
        except IndexError:
            return None
    last_checkout = property(_last_checkout)

    def _last_activity(self):
        try:
            return self.history.all()[0].timestamp
        except IndexError:
            return None
    last_activity = property(_last_activity)

И затем вы можете использовать их как обычно:

    some_item.last_checkout

Наконец, ваш метод извлечения не является переопределением save, поэтому неуместно вызывать super(ItemObject, self).save().Просто используйте self.save() вместо.

...