Django:从User模型中自动捕获用户名。不是PK

时间:2016-02-25 18:01:47

标签: python django

我想捕获当前登录用户的用户名,而不是pk。

incident.username = request.user不起作用

incident.username = request.user.username不起作用

incident.username = request.username不起作用

Uggg。这不可能是这么难。

models.py

class Incident(models.Model):
    username = models.ForeignKey(User)      ## field in db will be auto-populated by view.py
    date_reported = models.DateField()      ## field in db will be auto-populated by view.py
    date_occurred = models.DateField()
    number_of_samples_affected = models.IntegerField()
    capa = models.CharField(max_length=9)
    title =  models.CharField(max_length=100)
    description = models.TextField()
    status = models.ForeignKey(Status)      ## field in db will be auto-populated by view.py to "Open" at form submission
    category = models.ForeignKey(Category)                                      
    lab = models.TextField(Lab)

views.py

from submit_app.forms import IncidentForm
from submit_app.models import Incident, Status
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
import datetime
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse

@login_required(login_url='/login/')
def submit(request):

    if request.GET:
        form = IncidentForm()
        template = 'submit.html'
        context = {'form': form}
        return render(request, template, context)

    # if this is a POST request we need to process the form data
    if request.POST:

        # create a form instance and populate it with the data from the request:
        form = IncidentForm(request.POST)

        # check whether it's valid:
        if form.is_valid():
            incident = form.save(False)                         # creating an incident object, but not saving it to the db just yet
            incident.username = request.user                    # auto capture logged in user
            incident.date_reported = datetime.date.today()      # auto capture the incident submission date
            incident.status = Status.objects.get(status="open") # trying to auto populate status with 'Open' upon submission (foreign key)
            incident.save()
            return HttpResponseRedirect(reverse('dashboard_app:dashboard'))

    form = IncidentForm()
    template = 'submit.html'
    context = {'form': form}
    return render(request, template, context)

3 个答案:

答案 0 :(得分:2)

目前,您的模型与User模型具有外键关系,默认情况下与主键字段相关。要更改该内容并与username字段本身相关联,请在模型to_fieldmakemigrations中添加migrate关键字参数。

username = models.ForeignKey(User,to_field='username')  

之后,您可以通过request.user.username访问当前请求的用户,假设usernameUser的字段/属性(而不是相关的)模型)。

...

但是,通常不需要这样做。您仍然可以与User模型(通过PK构建的关系)相关联,并从那里访问用户名。最简单的方法是创建一个读取用户名的方法。

class Incident(models.Model):
    user = models.ForeignKey(User, related_name='incidents')
    def username(self):
        if self.user:
            return getattr(self.user,'username',None) # attempt to access username

... 

>>> first_incident = Incident.objects.create(user=User.objects.get(username='a'))
>>> print(first_incident.username())
a

答案 1 :(得分:1)

有一些明显的混乱。 Incident.username是User模型的外键,因此需要为其分配User对象,而不仅仅是用户名。因为force_reapprove应该有效。您可以稍后通过访问incident.username = request.user来访问用户名,但我会将该字段重命名为user以避免混淆。如果这不起作用,某些东西不能正常工作。如果您发布了错误,这将有所帮助。

答案 2 :(得分:0)

您应该使用自定义用户模型并将usename字段指定为主键。但是在django中,模型的抽象基类不能具有“覆盖字段”,因此您需要升级AbstractBaseUser而不是AbstractUser。您最终可能会得到AbstractUser代码(https://github.com/django/django/blob/1.8.9/django/contrib/auth/models.py#L378)的副本  只更改了一行:

来自django.contrib.auth.models导入AbstractBaseUser,PermissionsMixin,验证器,UserManager

class MyUser(AbstractBaseUser, PermissionsMixin):
    username = models.CharField(_('username'), max_length=30, unique=True,
        primary_key=True, ## the only difference from AbstractUser        help_text=_('Required. 30 characters or fewer. Letters, digits and '
                    '@/./+/-/_ only.'),
        validators=[
            validators.RegexValidator(r'^[\w.@+-]+$',
                                      _('Enter a valid username. '
                                        'This value may contain only letters, numbers '
                                        'and @/./+/-/_ characters.'), 'invalid'),
        ],
        error_messages={
            'unique': _("A user with that username already exists."),
        })
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
    last_name = models.CharField(_('last name'), max_length=30, blank=True)
    email = models.EmailField(_('email address'), blank=True)
    is_staff = models.BooleanField(_('staff status'), default=False,
        help_text=_('Designates whether the user can log into this admin '
                    'site.'))
    is_active = models.BooleanField(_('active'), default=True,
        help_text=_('Designates whether this user should be treated as '
                    'active. Unselect this instead of deleting accounts.'))
    date_joined = models.DateTimeField(_('date joined'), default=timezone.now)

    objects = UserManager()

    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = ['email']

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')
        abstract = True

    def get_full_name(self):
        """
        Returns the first_name plus the last_name, with a space in between.
        """
        full_name = '%s %s' % (self.first_name, self.last_name)
        return full_name.strip()

    def get_short_name(self):
        "Returns the short name for the user."
        return self.first_name

    def email_user(self, subject, message, from_email=None, **kwargs):
        """
        Sends an email to this User.
        """
        send_mail(subject, message, from_email, [self.email], **kwargs)

在此之后,您将能够将FK字段指向用户名字段。但你真的需要这个吗?为什么你需要这样的FK?主键最好是“静态”。通过使用用户名作为主键,您将无法更改用户名。

我可以想象出这样一个要求的几个原因:

  1. 您希望您的事件指向特定用户名而不是实际用户(可能您的用户实例可能会被删除,之后会使用相同的用户名重新创建?)。这很奇怪但可以完成:使用username = CharField(...)并使用getter和setter为user指定属性。

    class Incident(models.Model):
        username = models.CharField(max_length=30)
    
        @property
        def user(self):
            return User.objects.get(username=self.username)
    
        @user.setter
        def user(self, user):
            if user.is_authenticated():
                self.username = user.username
            else:
                self.username = '#anonymous'  # by default '#' is disallowed in username. You can also make your username nullable
    
  2. 您想“优化”数据库调用(不查询用户表)。在这种情况下,您最好使用预取或非规范化:

    from django.db import models
    
    # prefetch user with only "username" field. Assuming that you have `Incident.user = models.ForeignKey(...)`. Read https://docs.djangoproject.com/en/1.9/topics/db/managers/ and https://docs.djangoproject.com/en/1.9/ref/models/querysets/#prefetch-related
    class IncidentManager(models.Manager):
        def get_queryset(self):
            return super(IncidentManager, self).get_queryset().prefetch_related(models.Prefetch('user', queryset=User.objects.all().only('username'))
    
    class Incident(models.Model):
        user = models.ForeignKey(User)
        # ...
        objects = IncidentManager()
    

    如果是非规范化,你应该为User模型的post_save和post_delete信号创建接收器,该信号应该用实际用户名更新Incident.username字段。您还必须为Incident的post_save / post_delete创建类似的信号接收器(或者您可以修改Incident.saveIncident.delete方法)。您还可以为admin.models.LogAction post_save信号(from django.contrib.admin.models import DELETEION; if instance.action_flag == DELETEION and instance.content_type_id=get_content_type_for_model(Incident).pk:)创建信号接收器,因为django-admin的大量删除不会调用Incident.delete,也不会针对已删除的incidnets触发post_delete。如果您在项目中的任何位置使用User.object.update(username=something)或者数据直接在数据库中更改,那么即使此非规范化数据可能无效。