Django 通用创建视图。我们可以添加条件来保存表单中的数据吗?

Django generic create view . Can we add conditions to save data from the form?

这就是我的代码的样子,我想向 Borrower create view 添加一些条件,例如 book returns 0 的库存方法,然后在创建新的时不要在字段中列出该书borrower 或者如果不可能至少在将 borrower 添加到那本书时抛出一些错误。

models.py:

class Book(models.Model):
    id = models.UUIDField(primary_key=True, unique=True,
                          default=uuid.uuid4, editable=False)
    title = models.CharField(max_length=200)
    author = models.CharField(max_length=100)
    summary = models.TextField(
        max_length=1000, help_text="Enter a brief description of the book")
    isbn = models.CharField('ISBN', max_length=13,
                            help_text='13 Character https://www.isbn-international.org/content/what-isbn')
    genre = models.ManyToManyField(
        Genre, help_text="Select a genre for this book")
    language = models.ForeignKey(
        'Language', on_delete=models.SET_NULL, null=True)
    total_copies = models.IntegerField()
    pic = models.ImageField(blank=True, null=True, upload_to='books')

    def stock(self):
        total_copies = self.total_copies
        available_copies = total_copies - \
            Borrower.objects.filter(book=self).count()
        if available_copies > 0:
            return available_copies

        else:
            return 0

    
    def __str__(self):
        return self.title


class Borrower(models.Model):
    id = models.UUIDField(primary_key=True, unique=True,
                          default=uuid.uuid4, editable=False)
    student = models.ForeignKey('Account', on_delete=models.CASCADE)
    book = models.ForeignKey('Book', on_delete=models.CASCADE)
    issue_date = models.DateField(
        null=True, blank=True, help_text='YYYY-MM-DD', default=date.today)
    return_date = models.DateField(
        null=True, blank=True, help_text='YYYY-MM-DD')

    def __str__(self):
        return self.student.name.title()+" borrowed "+self.book.title.title()

    def fine(self):
        today = date.today()
        fine = 0
        if self.return_date <= today:
            fine += 5 * (today - self.return_date).days
        return fine

views.py:

class BorrowerView(LoginRequiredMixin, ListView):
    model=Borrower
    context_object_name='borrowers'
    template_name = 'library/borrower_list.html'


    def get_context_data(self, **kwargs):
        context=super().get_context_data(**kwargs)
        if self.request.user.is_admin or self.request.user.is_superuser:
            context['borrowers']=context['borrowers']
        else:
            context['borrowers']=context['borrowers'].filter(student = self.request.user.id)
        return context



class BorrowerCreate(LoginRequiredMixin, UserAccessMixin, CreateView):
    model=Borrower
    permission_required= 'borrowers.add_borrowers'
    fields='__all__'
    success_url=reverse_lazy('library:borrower-list')
    
    def form_valid(self, form):
        form.instance.user=self.request.user
        return super(BorrowerCreate, self).form_valid(form)



class BorrowerDetail(LoginRequiredMixin,  DetailView):
    model=Borrower()
    context_object_name='borrower'
    template_name='library/borrower.html'
    
class Book(models.Model):
     id = models.UUIDField(primary_key=True, unique=True,
                      default=uuid.uuid4, editable=False)
     title = models.CharField(max_length=200)
     author = models.CharField(max_length=100)
     summary = models.TextField(
    max_length=1000, help_text="Enter a brief description of the book")
    isbn = models.CharField('ISBN', max_length=13,
                        help_text='13 Character https://www.isbn-international.org/content/what-isbn')
    genre = models.ManyToManyField(
    Genre, help_text="Select a genre for this book")
    language = models.ForeignKey(
    'Language', on_delete=models.SET_NULL, null=True)
    total_copies = models.IntegerField()
    pic = models.ImageField(blank=True, null=True, upload_to='books')
    #new, use this to keep track of available books
    available_copies = models.IntegerField()

   def __str__(self):
       return self.title

当任何借阅者借出本书的副本时,您将从总副本中减去它。

class BorrowerCreate(LoginRequiredMixin, UserAccessMixin, CreateView):
    model=Borrower
    permission_required= 'borrowers.add_borrowers'
    fields='__all__'
    success_url=reverse_lazy('library:borrower-list')

   #remember to get the object using slug or 404 
    def form_valid(self, form):
        instance = form.save(commit=False)
        instance.user = self.request.user
        book = Book.objects.get(id=instance.book.id)
        #get the book id from the form and check if the book is still available, then subtract.
        if book.available_copies > 0:
           book.available_copies -= 1
           book.save()
           instance.save()
           message.success(self.request, _("successful")
        message.error(self.request, _("Book not in stock")
        return super(BorrowerCreate, self).form_valid(form)

如果用户 return 这本书并单击 returned。您可以通过添加到可用副本来执行类似的操作。

这不是解决方案,您可以使用同时处理借用和 return 的方法编写胖模型。像这样

def borrow(self):
    self.available_copies -= 1

def returned(self):
    self.available_copies += 1

您可以在不同的视图中调用这两个方法,或者定义一个调用它们的信号 pre_save

是: 与其定义一个名为 stock 的新方法,不如将 stock 添加为字段而不是进行数据库查询。

第二名: 在同一个 class 中调用 class 并不是在 class.

中进行查询的最佳方式

第三: 添加对象的同时添加条件,需要像这样重写对象的保存方法

def save(self, *args, **kwargs):
    # do_something() here.....
    # then
    return super().save(*args, **kwargs)

以上代码将使您能够在保存对象之前执行任何操作。

另一种方法是在 form_valid 函数中执行此操作。

def form_valid(self, form):
    instance = form.save(commit=False)
    # commit = False will make sure that the form is not saved
    # then you can now query the database and check conditions like
    if Borrower.object.all().count() > 0:
       instance.save()
       messages.success(self.request, _("saved successfully")
    else:
       messages.error(self.request, _("Error")
    return redirect("URL")