[英]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
关键字参数模型, makemigrations
和migrate
。
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). 之后,假设
username
是User
的字段/属性(而不是相关模型),您将能够通过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.user
, incident.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: 我可以想象有这样的要求的几个原因:
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
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.save
和Incident.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.