Django编辑表单无法在带用户字段的form.save上验证

问题描述

我最近在我的游戏模型中添加一个用户”字段。我可以创建一个运行良好的新游戏;就是在我希望允许用户编辑我遇到问题的游戏实例时。我的观点是在form = GameForm(request.POST,instance=game)调用game = Game.objects.get(pk=id)。表单已预先填充了正确的数据,但是在提交表单后,无论是否有更新,表单都不会生效。它将其视为POST,但无法进入if form.is_valid()条件内部。自从我添加用户字段以来,这一直是。我正在使用认的Django用户模型,字段名称称为“所有者”。由于用户可以拥有许多游戏,并且游戏可以被许多用户拥有,因此将其设置为ManyToManyField(User,blank=True)。 Django构成了“多对多”“通过”表,但是我不希望用户能够更改谁拥有什么。我将其作为forms.py中的隐藏字段,因此用户无法更改。

模型

class Game(models.Model):
    game_title = models.CharField(max_length=100,verbose_name='Game Title',db_column='game',blank=False,null=False,unique=True)
    game_developer = models.CharField(max_length=100,verbose_name='Developer',db_column='developer',blank=True,null=True)
    game_release = models.DateField(max_length=50,verbose_name='Release Date',db_column='release_date',null=True)
    rating = models.IntegerField(verbose_name='Game rating',db_column='rating',choices=INT_CHOICES,null=True)
    game_genre = models.CharField(max_length=100,verbose_name='Genre',db_column='genre',null=True,choices=GENRE_CHOICES)
    game_platform = models.CharField(max_length=100,verbose_name='Game Platform',db_column='platform',choices=PLATFORM_CHOICES)
    game_esrb = models.CharField(max_length=100,verbose_name='ESRB rating',db_column='esrb',null=True)
    owner = models.ManyToManyField(User,blank=True)

    objects = models.Manager()

    def __str__(self):
        return self.game_title

    class Meta:
        db_table = 'tbl_games'
        verbose_name = 'Game'

查看

# Allows the user to update game information
def editGame(request,id):
    # Finds the user selected game by game id
    game = Game.objects.get(pk=id)
    user = request.user.id

    if request.method == 'POST':
        print("Seen as POST")
        # Create game instance pre-populated into a form
        form = GameForm(request.POST,instance=game)
        if form.is_valid():
            print("Form is valid!")
            # Saves the edits without saving to the dB
            form.save()
            messages.success(request,'Game successfully updated!')
            return redirect('library')
    else:
        print('Seen as GET')
        form = GameForm(instance=game)
    print("Page loaded")
    context = {'form': form}
    return render(request,'library/editGame.html',context)

表格

class GameForm(ModelForm):

    game_esrb = forms.CharField(required=False,widget=HiddenInput)
    owner = forms.Hiddeninput()

    def __init__(self,*args,**kwargs):
        super(GameForm,self).__init__(*args,**kwargs)

    class Meta:
        model = Game
        fields = [
            'game_title','game_developer','rating','game_release','game_genre','game_platform','game_esrb','owner'
        ]
        widgets = {
            'rating': Select(attrs={'choices': INT_CHOICES}),'game_genre': Select(attrs={'choices': GENRE_CHOICES}),'game_platform': Select(attrs={'choices': PLATFORM_CHOICES}),'esrb_rating': Hiddeninput(),'owner': HiddenInput
        }
        help_texts = {
            'rating':
                'Key: 1 - Bad | 2 - Okay | 3 - Average | 4 - Good | 5 - Great'
        }

模板

{% block appcontent %}
<div class="height">
  <form method="POST">
    {% csrf_token %}
    {{ form|crispy }}
    <div class="form-btn">
      <a class=" btn btn-secondary cancel" type="button" href="{% url 'library' %}">Cancel</a>
      <button class="update btn btn-primary" type="submit">Update</button>
    </div>
  </form>
</div>
{% endblock %}

解决方法

首先,从分配给GameForm类中的字段的列表中删除“所有者”:

class Meta:
    model = Game
    fields = [
        'game_title','game_developer','rating','game_release','game_genre','game_platform','game_esrb',# 'owner' removed
    ]

原因如下:在ModelForm中,您只需要在表单中命名要用作输入字段的游戏字段 。在您的情况下,您实际上并不希望将“所有者”字段用作输入字段,因此只需将其排除在此列表之外即可。这将实现您规定的禁止用户修改所有者字段的目标。

现在让我们查看并修改您的视图:

  1. 您可以在视图中执行此操作:“ user = request.user.id”这将为属性'user'分配一个int。我猜想您想要一个用户对象,而不是一个int,但目前还不清楚,因为您实际上从未在视图中的任何地方使用此属性。

  2. 这是需要发生的事情:视图需要将用户对象连接到游戏对象,然后保存游戏对象。我们已经确保“所有者”不会在表单中显示为输入字段。现在,我们需要为该字段手动提供一个值,然后保存该对象。下面,我们在您视图中的is_valid()调用之后执行此操作:

     def editGame(request,id):
         # Finds the user selected game by game id
         game = Game.objects.get(pk=id)
    
         if request.method == 'POST':
             print("Seen as POST")
             # Create game instance pre-populated into a form
             form = GameForm(request.POST,instance=game)
    
             if form.is_valid():
                 print("Form is valid!")
                 form.save()
    
                 game.owner.add(request.user)   # update the game's owner field,assigning the current user
                 game.save()
    
                 messages.success(request,'Game successfully updated!')
                 return redirect('library')
    

总结并阐明:所有这些工作的最终结果是更新了Game对象。用户通过ModelForm更新值。该视图将验证这些更改,然后保存表格,并保存相应的游戏对象。然后,我们将用户手动分配到游戏的“所有者”字段,因为我们有意将该字段从表单中删除。然后我们再次保存游戏,以便此更改包含在对象的“最终”状态。