2011-11-18 18 views
1

我正在圖書館系統來管理我們辦公室的某些項目,我並不需要一個全面的集成圖書館系統,所以我決定手卷一個Django的。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: saving multiple modelforms simultaneously (complex case)

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

django update modelform

所以是我在正確的軌道上帶有幾分改變save()方法,或將一個的ModelForm子類是要走的路嗎?

編輯/ UPDATE:非常感謝@ChrisPratt!

所以我試圖讓Chris Pratt的建議顯示ItemHistory的工作,但是當我嘗試在頁面上呈現它時,我得到一個AttributeError,它聲明「'User'對象沒有屬性'timestamp'」。所以我的問題是,爲什麼它抱怨一個User對象時last_activityItemObject對象的屬性?

我的觀點:

@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領域,其目標用戶。這就是爲什麼!

回答

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

現在,您的結帳方式有許多問題。首先,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()

+0

只是一些評論:1)我建議'related_name ='history''作爲'ItemObject.history',因爲它是'User'的歷史。 2)你可以做'some_item.history.create(user = some_user,activity = ItemHistory.CHECKED_OUT)'而不是通過'ItemHistory.objects'? 3)我建議使用'@ property'裝飾器來處理'last_checkout'。 –

+0

謝謝克里斯普拉特!我想我應該澄清,但我們並沒有強制簽出限額。我喜歡你對歷史做的事情,現在就試試看!編輯:還要感謝邁克德西蒙! – HristosT

+0

我爲相關名稱選擇了'item_history'而不是'history',因爲可以預見'User'可能會有其他「歷史」與其關聯。 'ItemObject'將只有一個「歷史」,所以縮寫形式在那裏很好。 –