在基于 Class 的视图中限制经过身份验证的用户的“UpdateView”数据集

Restrict `UpdateView` dataset for authenticated user in Class Based Views

我有一个 Django 项目,我在其中使用 OneToOneField 扩展了用户以拥有一个配置文件。我正在使用 CBV UpdateView,它允许用户更新他们的个人资料。他们为此访问的 URL 是 ../profile/user/update。我遇到的问题是,如果用户输入另一个用户名,他们可以编辑其他人的个人资料。我如何限制 UpdateView 以便经过身份验证的用户只能更新他们的个人资料。我试图做一些事情来确保 user.get_username == profile.user 但没有运气。

Models.py

from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.core.urlresolvers import reverse

class Profile(models.Model):
    # This field is required.
    SYSTEM_CHOICES = (
        ('Xbox', 'Xbox'),
        ('PS4', 'PS4'),
    )
    system = models.CharField(max_length=5,
                                    choices=SYSTEM_CHOICES,
                                      default='Xbox')
    user = models.OneToOneField(User)
    slug = models.SlugField(max_length=50)
    gamertag = models.CharField("Gamertag", max_length=50, blank=True)
    f_name = models.CharField("First Name", max_length=50, blank=True)
    l_name = models.CharField("Last Name", max_length=50, blank=True)
    twitter = models.CharField("Twitter Handle", max_length=50, blank=True)
    video = models.CharField("YouTube URL", max_length=50, default='JhBAc6DYiys', help_text="Only the extension!", blank=True)
    mugshot = models.ImageField(upload_to='mugshot', blank=True)

    def __unicode__(self):
            return u'%s' % (self.user)

    def create_user_profile(sender, instance, created, **kwargs):
        if created:
            Profile.objects.create(user=instance, slug=instance)

    post_save.connect(create_user_profile, sender=User)

    def get_absolute_url(self):
        return reverse('profile-detail', kwargs={'slug': self.slug})

Views.py

from django.shortcuts import render
from django.views.generic import DetailView
from django.views.generic.edit import UpdateView
from django.views.generic.list import ListView

from profiles.models import Profile


class ProfileDetail(DetailView):

    model = Profile

    def get_context_data(self, **kwargs):
        context = super(ProfileDetail, self).get_context_data(**kwargs)
        return context

class ProfileList(ListView):
    model = Profile
    queryset = Profile.objects.all()[:3]

    def get_context_data(self, **kwargs):
        context = super(ProfileList, self).get_context_data(**kwargs)
        return context

class ProfileUpdate(UpdateView):
    model = Profile
    fields = ['gamertag', 'system', 'f_name', 'l_name', 'twitter', 'video', 'mugshot']
    template_name_suffix = '_update'

    def get_context_data(self, **kwargs):
        context = super(ProfileUpdate, self).get_context_data(**kwargs)
        return context

Admin.py

from django.contrib import admin
from models import Profile

class ProfileAdmin(admin.ModelAdmin):
    prepopulated_fields = {'slug': ('user',), }

admin.site.register(Profile, ProfileAdmin)

Urls.py 个人资料应用

from django.conf.urls import patterns, url
from django.contrib.auth.decorators import login_required
from profiles.views import ProfileDetail, ProfileUpdate

urlpatterns = patterns('',
    url(r'^(?P<slug>[-_\w]+)/$', login_required(ProfileDetail.as_view()), name='profile-detail'),
    url(r'^(?P<slug>[-_\w]+)/update/$', login_required(ProfileUpdate.as_view()), name='profile-update'),
)

Profile_update.html

{% extends "base.html" %} {% load bootstrap %}

{% block content %}

{% if user.is_authenticated %}

  <h1>Update your profile</h1>

  <div class="col-sm-4 col-sm-offset-4">
    <div class="alert alert-info alert-dismissible" role="alert">
      <button type="button" class="close" data-dismiss="alert" aria-label="Close">
        <span aria-hidden="true">&times;</span>
      </button>
      <strong>Heads up!</strong> Other users can find you easier if you have a completed profile.
    </div>
    <form enctype="multipart/form-data" method="post" action="">{% csrf_token %}
      {{ form|bootstrap }}
      <input class="btn btn-default" type="submit" value="Update" />
    </form>
  </div>


{% else %}
<h1>You can't update someone elses profile.</h1>
{% endif %}

{% endblock %}

这样的事情怎么样:

from django.contrib.auth.views import redirect_to_login


class ProfileUpdate(UpdateView):
    [...]

    def user_passes_test(self, request):
        if request.user.is_authenticated():
            self.object = self.get_object()
            return self.object.user == request.user
        return False

    def dispatch(self, request, *args, **kwargs):
        if not self.user_passes_test(request):
            return redirect_to_login(request.get_full_path())
        return super(ProfileUpdate, self).dispatch(
            request, *args, **kwargs)

在此示例中,用户被重定向到默认 LOGIN_URL。但是你可以很容易地改变它。将用户重定向到他们自己的个人资料。

  • 你的template.html:

{% if request.user.is_authenticated and profile.user == request.user %}
your form
{% else %}
u cannot edit that profile - its not yours...
{% endif %}

为避免在使用 Class 基于视图 (CBV) 时访问与已连接用户无关的数据,您可以使用 动态过滤 并在 model 属性上定义 queryset

如果您在 auth.models.user 上有一个 book.models 和一个 ForeignKey(此处名为 user),您可以像这样轻松地限制访问:

# views.py
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import ListView
from books.models import Book

class BookList(LoginRequiredMixin, ListView):

    def get_queryset(self):
        return Book.objects.filter(user=self.request.user)

请参阅有关 CBV - Viewing subsets of objects

的文档中的更多说明

Specifying model = Publisher is really just shorthand for saying queryset = Publisher.objects.all(). However, by using queryset to define a filtered list of objects you can be more specific about the objects that will be visible in the view.

[…]

Handily, the ListView has a get_queryset() method we can override. Previously, it has just been returning the value of the queryset attribute, but now we can add more logic. The key part to making this work is that when class-based views are called, various useful things are stored on self; as well as the request (self.request) this includes the positional (self.args) and name-based (self.kwargs) arguments captured according to the URLconf.