首页 > 解决方案 > 基于类的视图总和用于基于类的表单验证(没有 forms.py)

问题描述

我已经为此工作了几天,并在我的幻想体育网站上重新设计了我希望如何处理此功能:

目标:限制幻想所有者名单上允许的玩家数量。

Django 开箱即用的用户 = 所有者

# models.py
class Player(models.Model):
    player_full = models.CharField(max_length=50)
    player_owner = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)
    image = models.ImageField(default='default_player.jpg', upload_to='player_pics')
    player_unit_value = models.PositiveSmallIntegerField(default=1, validators=[MinValueValidator(1),
                                                                            MaxValueValidator(1),])

这是views.py上的课程,我想这是表单的运作方式。我的项目的这个区域没有forms.py。

# views.py
class PlayersUpdateView(LoginRequiredMixin, UpdateView, Player):
    model = Player
    fields = []
    template_name = 'blog/player_form.html'  # <app>/<model>_<viewtype>.html
    context_object_name = 'players'

    # this def makes sure only the post author can edit the post
    def test_func(self, **kwargs):
        # this makes sure it is the exact post we are updating
        player = self.get_object()
        if self.request.user == player.player_owner:
            return True
        return False

    def form_valid(self, form):
        form.instance.player_owner = self.request.user
        return super().form_valid(form)

因此,我有一个功能可以将玩家“添加”到您的花名册中,它会验证您是否已登录,然后 -- 当提交时 -- Player.player_owner 字段从None用户更新。我想做的是——当用户尝试将新玩家添加到他们的名单中时,系统会检查用户已经拥有多少玩家(也就是在运行时该用户被列为 player_owner 的次数) Player 模型的整个数据库),如果玩家已经拥有最多 15 名玩家,则会吐出一个错误。否则,它允许“添加”通过。

如果您在 Player 模型上注意到我还有一个名为的字段player_unit_value,它普遍停留在 int 1。我能够使用该字段在团队的花名册页面上的用户花名册编号上生成总球员。因此,在每个所有者的名单页面上,它会显示他们拥有的球员总数。这是我用来完成它的 views.py 类(感谢@ruddra 的代码):

class UserPlayerListView(ListView):
    model = Player
    template_name = 'blog/user_players.html' # <app>/<model>_<viewtype>.html
    context_object_name = 'players'
    paginate_by = 20

    def get_queryset(self):
        user = get_object_or_404(User, username=self.kwargs.get('username'))
        return Player.objects.filter(player_owner=user).order_by('-player_sal_19_20')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        player_list = list(context['players'])  # evaluates the query
        context['sum_of_sal_19_20'] = sum([x.player_sal_19_20 for x in player_list])  # pythonic sum calculation
        context['player_count'] = sum([x.player_unit_value for x in player_list])
        context['players'] = player_list
        return context

然后我可以使用 html 标签 {{ player_count }}

所以这证明了这个数字(player_unit_value对于每个在他们的 player_owner 字段中列出 User 的玩家来说始终为 1)可以计算并显示在前端。我想要做的是在用户尝试添加玩家时访问该号码,并且 - 如果该号码高于 14 - 拒绝收购。我只是还没弄清楚如何在PlayerUpdateView(作为表单工作)而不是UserPlayerListView.

我在这里看到了很多关于如何进行自定义表单验证的讨论,我需要为项目的这个区域提供一个实际的 forms.py 文件。我有一个与我的用户区域关联的 forms.py 文件,它处理用户配置文件表单(与用户名册页面不同)。我可以为已经工作的表单追溯设置 forms.py 文件吗?我还尝试创建自己的 validators.py 文件并在 player_owner 字段上使用自定义验证器,但没有成功。


编辑代码:

视图.py

    def form_valid(self, form):
        if self.request.method == 'POST':
            form = AddPlayerForm(self.request.POST)
            if form.is_valid():
                form.save(commit=False)
                current_user = self.request.user
                if Player.objects.filter(player_owner=current_user).count() > 14:
                    messages.info(self.request, f'Your roster is full! Player not added.')
                    return redirect('profile')
                else:
                    form.instance.player_owner = self.request.user
                    return super().form_valid(form)

表格.py

class AddPlayerForm(forms.ModelForm):
    class Meta:
        model = Player
        fields = []

笔记:

出于某种原因,为了避免错误,我不得不在 view.py 上所有 request 的使用前包含“self”。此外,在我尝试添加此表单验证之前,我能够使用此“PlayerUpdateView”类表单和单独的“PlayerDropView”类表单在视图上通过这些基于类的表单编辑播放器模型。两者在启动时分别完成一个功能:将选定的 Player 模型字段“player_owner”从 None 切换到已登录的 User,反之亦然。因此,我能够将旧信息传递到表单中,然后将其作为更新发布,在需要开始验证之前,我无需深入研究它。

所以就目前而言:代码确实将用户限制为 15 名或更少的球员,但现在当添加球员时,会在后端创建一个球员的空白实例并将用户分配为 player_owner,而玩家的 player_owner 字段保持为无。


最终编辑:

@Josh 提供了这个解决方案。请参阅他在答案中的注释:

视图.py

def player_update(request, id,):
    player = Player.objects.get(id=id)
    current_user = request.user
    if Player.objects.filter(player_owner=current_user).count() <= 14:
        player.player_owner = current_user
        player.save()
        messages.success(request, f'Player added!')
        return redirect('blog-players')
    else:
        messages.warning(request, f'Your roster is full! Player not added.')
        return redirect('blog-players')

def players(request):
    context = {
        'players': Player.objects.all(),
    }
    return render(request, 'blog/players.html', context)

问题:由于某种原因,当我尝试使用else: message.error红色轮廓时,输出中没有出现。为什么?此外,我无法让我的重定向转到用户的特定花名册页面。为什么?

网址.py

    path('user_players/<str:username>/', UserPlayerListView.as_view(), name='user-players'),

    path('players/<id>/updating/', views.player_update, name="player-updating")

player.html 上的 html

<a class="dropdown-item" href="{% url 'player-updating' player.id %}">Sign</a>

标签: pythondjangodjango-modelsdjango-formsdjango-views

解决方案


您可以在 views.py 或 forms.py 中执行您的逻辑。

下面应该可以工作,虽然没有亲自测试过。

视图.py

if request.method == 'POST':
        form = forms.AddPlayer(request.POST)
        if form.is_valid():
            instance = form.save(commit=False)
            current_user = request.user
            if Player.objects.filter(user_id=current_user).count() > 14:
                 # do something here, such as return error or redirect to another page
            else:
                 instance.save()

或形式: forms.py

class AddPlayerForm(forms.ModelForm):
    class Meta:
        model = Player
        fields = ['name', 'number']
    def clean(self):
        current_user = request.user
        if Player.objects.filter(user_id=current_user).count() <= 14:
            pass
        else:
            raise ValidationError('Your team is full!')

编辑:更新。

所以我会这样做。我不是这方面的专家,所以我确信有更好的方法,但这会奏效并且不会太笨重。

如此大的画面:有一个包含所有“玩家”的页面(或分页页面),然后对于每个玩家,有一个链接运行一些逻辑来检查当前用户的名单,如果 14 或更少,添加到列表中。

网址.py

path('player_list', views.player_list, name="playerlist"),
path('player/<id>/update', views.player_update, name="playerupdate"),

视图.py

def player_list(request)
    player_list = Player.objects.all()
    return render(request, 'player_list.html', {'player_list': player_list}

def player_update(request, id):
    player = Player.objects.get(id=id)
    current_user = request.user 
    if Player.objects.filter(user_id=current_user).count() <= 14:
         player.player_owner = current_user
         player.save()
         return redirect('roster')
    else:
         return redirect('error')

player_list.html

{% for player in player_list %}
{{player.name}} -- <a href="/player/{{player.id}}/update"> Click here to claim this player!</a>
{% endfor %}

推荐阅读