简体   繁体   English

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

[英]Django: auto capturing username from the User model. not pk

I want to capture the username of the user currently logged in, NOT pk. 我想捕获当前登录用户的用户名,而不是pk。

incident.username = request.user doesn't work incident.username = request.user不起作用

incident.username = request.user.username doesn't work incident.username = request.user.username不起作用

incident.username = request.username doesn't work incident.username = request.username不起作用

Uggg. 哎呀 This can't be this hard. 这可以不难。

models.py 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 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)

Right now, your model has a foreign key relation to the User model, which by default relates to the primary key field. 现在,您的模型与User模型具有外键关系,默认情况下与主键字段相关。 To change that and relate to the username field itself, add a to_field keyword argument to your model, makemigrations , and migrate . 要改变这一点,涉及到username字段本身,添加to_field关键字参数模型, makemigrationsmigrate

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

Afterwards, you'll be able to access the user for the current request via request.user.username , assuming that username is a field/attribute of User (and not a related model). 之后,假设usernameUser的字段/属性(而不是相关模型),您将能够通过request.user.username访问当前请求的User

... ...

However, there's generally no need to do this. 但是,通常不需要这样做。 You can still relate to the User model (relation built via PK) and access the username from there. 您仍然可以关联到User模型(通过PK建立的关系)并从那里访问用户名。 Easiest way to do this is perhaps to create a method to read the username. 最简单的方法可能是创建一种读取用户名的方法。

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

There is some obvious confusion. 有一些明显的混乱。 Incident.username is a foreign key to a User model, so it needs to be assigned a User object, not just a username. Incident.username是User模型的外键,因此需要为其分配一个User对象,而不仅仅是用户名。 For that incident.username = request.user should work. 对于该incident.username = request.userincident.username = request.user应该起作用。 You can later access the user name by accessing incident.username.username , although I would rename the field to user to avoid confusion. 以后您可以通过访问incident.username.username来访问用户名,尽管我会将该字段重命名为user以避免混淆。 If this doesn't work, something is not working as it should. 如果这不起作用,则说明某些东西无法正常工作。 It would help if you posted the error you are getting. 如果您发布了您遇到的错误,这将有所帮助。

You should use custom user model and specify usename field to be primary key. 您应该使用自定义用户模型,并将usename字段指定为主键。 But in django abstract base classes for models can't have "overriden fields" so you will need to sublcass AbstractBaseUser instead of AbstractUser . 但是在django中,模型的抽象基类不能具有“覆盖字段”,因此您将需要Sublcass AbstractBaseUser而不是AbstractUser You may eventually end up with a copy of AbstractUser code ( https://github.com/django/django/blob/1.8.9/django/contrib/auth/models.py#L378 ) with just one line changed: 您可能最终会得到一个AbstractUser代码的副本( https://github.com/django/django/blob/1.8.9/django/contrib/auth/models.py#L378 ),只更改了一行:

from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, validators, UserManager 从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)

After this you will be able to point FK fields to username field. 之后,您将可以将FK字段指向用户名字段。 But do you realy need this? 但是您真的需要这个吗? Why do you need to have such FK? 为什么需要这样的FK? Primary keys should better be "static". 主键最好是“静态”的。 By using username as primary key you will have problems changing your usernames. 通过使用用户名作为主键,您将无法更改用户名。

I can imagine several reasons for such a requirement: 我可以想象有这样的要求的几个原因:

  1. You want your incidents to point specific username instead of actual user (maybe your instances of User may be deleted and later recreated with same username?). 您希望事件指向特定的用户名而不是实际用户(也许您的User实例可能已删除,后来又使用相同的用户名重新创建?)。 This is strange but can be done: use username = CharField(...) and also specify property for user with getter and setter. 这很奇怪,但是可以做到:使用username = CharField(...)并使用getter和setter为用户指定属性。

     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. you want to "optimize" database calls (to not query users table). 您要“优化”数据库调用(不查询用户表)。 In this case you'd better use prefetching or denormalization: 在这种情况下,您最好使用预取或非规范化:

     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() 

    In case of denormalization you should create receiver for post_save and post_delete signals for User model which should update Incident.username field with actual username. 在非规范化的情况下,您应该为User模型的post_save和post_delete信号创建接收器,这应使用实际的用户名更新Incident.username字段。 You must also create similar signal receivers for Incident's post_save/post_delete (or you can modify Incident.save and Incident.delete methods). 您还必须为Incident的post_save / post_delete创建类似的信号接收器(或者您可以修改Incident.saveIncident.delete方法)。 You may also create signal receiver for admin.models.LogAction post_save signal ( from django.contrib.admin.models import DELETEION; if instance.action_flag == DELETEION and instance.content_type_id=get_content_type_for_model(Incident).pk: ) because mass deletion from django-admin does not call Incident.delete and does not trigger post_delete for deleted incidnets. 您还可以为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的管理不叫Incident.delete并不会触发post_delete已删除incidnets。 And even after this denormalized data may be invalid if you use User.object.update(username=something) anywhere in your project or if data is changed directly in database. 而且即使在这种非规范化的数据之后,如果您在项目中的任何地方使用User.object.update(username=something)或直接在数据库中更改数据,数据也可能无效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM