Django驱动的图书馆结账系统

时间:2011-11-18 15:56:40

标签: python django forms checkout

我正在建立一个图书馆系统来管理我们办公室的某些项目,我不需要一个完整的集成图书馆系统,所以我决定用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_hasEmailField,因为我无法获得CharfField使用登录用户的名称填充,但我认为使用OneToOneField可能更接近“正确”的方式来执行此操作..虽然who_hasEmailField,但以下方法工作:

    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的子类,但我在这里看到的所有案例似乎都没有适用于我我想做;我的表格将是一个按钮,就是这样。以下是我看到的一些问题:

Django: saving multiple modelforms simultaneously (complex case)

(Django) (Foreign Key Issues) model.person_id May not be NULL

django update modelform

我是否正确地使用了一种改进的save()方法,或者是否可以使用ModelForm子类?

编辑/更新:非常感谢@ChrisPratt!

所以我试图让Chris Pratt建议显示ItemHistory,但是当我尝试在页面上呈现它时,我得到一个AttributeError,指出“'User'对象没有属性'timestamp'” 。所以我的问题是,当Userlast_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 个答案:

答案 0 :(得分:3)

假设用户将自己登录并查看图书,那么您最想要的是ForeignKeyUser。一本书在任何给定时间只有一个User,但大概User也可以查看其他项目。如果存在某些限制,即使每个用户的限制实际上是一个,最好在模型的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.')

现在,checkout方法有很多问题。首先,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

然后,您可以获得完整的历史记录:

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_checkoutlast_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()代替。