Django 支持的图书馆借阅系统

发布于 2024-12-16 23:38:48 字数 3874 浏览 0 评论 0原文

我正在开发一个图书馆系统来管理我们办公室的某些项目,我不需要一个成熟的集成图书馆系统,所以我决定使用 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< /code> 因为我无法使用 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 的子类,但我在这里看到的所有案例似乎都不适合我想做的事情;我的表单是一个按钮,仅此而已。以下是我查看的一些问题:

Django:保存多个同时模型(复杂情况)

(Django) (外键问题) model.person_id 可能not be NULL

django update modelform

我走在正确的轨道上吗使用某种改变的 save() 方法,或者 ModelForm 子类是可行的方法吗?

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

因此,我试图让 Chris Pratt 关于显示 ItemHistory 的建议起作用,但是当我尝试在页面上呈现它时,我收到一个 AttributeError ,指出“'User'对象没有属性'timestamp'” 。所以我的问题是,当 last_activityItemObject 对象上的属性时,为什么它会抱怨 User 对象?

我的观点:

@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 领域,其目标是用户。这就是为什么!

I am working on a library system to manage certain items in our office, I don't need a full-blown integrated library system so I decided to hand roll one with Django.

Below is a simplified version of my model:

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)

At first I was using a method on ItemObject to process checking out an item to a user and who_has was an EmailField because I couldn't get a CharfField to populate with the logged in user's name, but I figured using a OneToOneField is probably closer to the "right" way to do this.. While who_has was an EmailField, the following method worked:

    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()

Now that I am using a OneToOneField this doesn't work, so I started looking at using a subclass of ModelForm but none of the cases I saw here on SO seemed to apply for what I am trying to do; my form would be a button, and that's it. Here are some of the questions I looked at:

Django: saving multiple modelforms simultaneously (complex case)

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

django update modelform

So was I on the right track with a sort of altered save() method, or would a ModelForm subclass be the way to go?

EDIT/UPDATE: Many thanks to @ChrisPratt!

So I am trying to get Chris Pratt's suggestion for showing ItemHistory to work, but when I try to render it on a page I get an AttributeError that states "'User' object has no attribute 'timestamp'". So my question is, why is it complaining about a User object when last_activity is an attribute on the ItemObject object ?

My view:

@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 })

I do not see why a User object is coming up at this point.

EDIT2: Nevermind, history is clearly a M2M field whose target is User. That's why!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

不念旧人 2024-12-23 23:38:49

假设用户将自己登录并签出书籍,那么您最可能需要的是 UserForeignKey。一本书在任何给定时间都只有一个用户,但大概用户也可以查看其他项目。如果存在某种限制,即使该限制实际上是每个用户一个,最好在模型的 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

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

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() 即可。

Assuming users will log in and check out books to themselves, then what you most likely want is a ForeignKey to User. A book will only have one User at any given time, but presumably Users could check out other items as well. If there is some limit, even if the limit is actually one per user, it would be better to validate this in the model's clean method. Something like:

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.')

Now, you checkout method has a number of issues. First, status should be a defined set of choices, not just random strings.

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)

Then, you can run your checks like:

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

You could use strings and a CharField instead if you like, as well. The key is to decouple the static text from your code, which allows much greater flexibility in your app going forward.

Next, history needs to be a ManyToManyField. Right now, your "history" is only who last checked the item out or what the last item the user checked out was, and as a result is pretty useless.

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

Which then allows you to get full histories:

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

To add a new history, you would do:

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

The auto_now_add attribute ensures that the timestamp is automatically set when the relationship is created.

You could then actually get rid of the last_checkout and last_activity fields entirely and use something like the following:

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)

And, you can then use them as normal:

    some_item.last_checkout

Finally, your checkout method is not an override of save so it's not appropriate to call super(ItemObject, self).save(). Just use self.save() instead.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文