英文:
Django forms passing request via get_form_kwargs fails to give form access to self.request.user
问题
Goal
我需要使用户能够通过表单进行验证(即通过 self.request.user)。
Approach Taken
我在我的视图中使用 get_form_kwargs() 函数,将请求传递给我的表单(建议参考这里:非常相似的问题)。
Problem
尽管遵循了上面链接的StackOverflow答案中概述的步骤,但我仍然收到错误消息 'NoneType' object has no attribute 'user'。
Code
views.py
class MemberDetailView(LoginRequiredMixin, generic.DetailView):
"""View class for member profile page"""
model = User
context_object_name = 'user'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs,)
context['form'] = JoinCommunityForm()
return context
class JoinCommunityFormView(FormView):
"""View class for join code for users to join communities"""
form_class = JoinCommunityForm
def get_form_kwargs(self, *args, **kwargs):
form_kwargs = super().get_form_kwargs(*args, **kwargs)
form_kwargs['request'] = self.request
return form_kwargs
def post(self, request, *args, **kwargs):
"""posts the request to join community only if user is logged in"""
if not request.user.is_authenticated:
return HttpResponseForbidden
return super().post(request, *args, **kwargs)
class MemberDashboardView(View):
"""View class to bring together the MemberDetailView and the JoinCommunityFormView"""
def get(self, request, *args, **kwargs):
view = MemberDetailView.as_view()
return view(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
view = JoinCommunityFormView.as_view()
form = JoinCommunityForm(request.POST)
if form.is_valid():
forms.py
引发错误的行是 user=self.request.user
from communities.models import Community, UserRoleWithinCommunity
class JoinCommunityForm(forms.Form):
pin = forms.RegexField(
'^[A-HJ-NP-Z1-9]$',
max_length=6,
label='',
widget=forms.TextInput(attrs={
'oninput': 'this.value = this.value.toUpperCase()'
}
)
)
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(JoinCommunityForm, self).__init__(*args, **kwargs)
def clean(self):
cleaned_data = super().clean()
pin = self.cleaned_data['pin']
test_join_code = f'{pin}'
if Community.objects.filter(join_code=test_join_code).exists():
# Make sure user is not already a member of the community
communityToJoin = Community.objects.get(join_code=test_join_code)
if UserRoleWithinCommunity.objects.filter(
community=communityToJoin,
user=self.request.user
).exists():
raise forms.ValidationError("You're already a member of that Community")
else:
return cleaned_data
else:
raise forms.ValidationError("Join code is incorrect")
Edit 1:
我尝试了Willem建议的方法,但仍然遇到相同的错误 'NoneType' object has no attribute 'user',这个错误追溯到我的表单中需要通过 self.request.user 访问用户的那一行。
所有 forms.py 代码仍然保持不变。我的 views.py 代码如下更改(现在我只有一个视图,而不是两个视图(表单视图和详细视图),它们合并到另一个视图中):
views.py
# 所有必要的导入语句
class MemberDetailView(
LoginRequiredMixin, FormMixin,
generic.DetailView, ProcessFormView
):
"""View class for member profile page"""
model = User
context_object_name = 'user'
form_class = JoinCommunityForm
success_url = '#communities-card'
template_name = 'home/terpuser_detail.html'
def get_queryset(self, *args, **kwargs):
qs = super().get_queryset(*args, **kwargs)
if not self.request.user.is_superuser:
qs = qs.filter(pk=self.request.user.pk)
return qs
def get_context_data(self, **kwargs):
context = super().get_context_data(
**kwargs,
demo=get_object_or_404(Scenario.objects, scenario_id='demo'),
today=date.today(),
)
return context
def form_valid(self, form):
# 这是我放置创建新的Community membership对象的代码的地方
return super().form_valid(self, form)
urls.py
from django.urls import path
from . import views
urlpatterns = [
path('member/<slug:slug>/', views.MemberDetailView.as_view(), name='member-detail')
]
error message
- 请求方法:POST
- 请求URL:http://127.0.0.1:8000/member/tron/
- Django版本:4.1.5
- 异常类型:AttributeError
- 异常值:'NoneType' object has no attribute 'user'
- 异常位置:../projDir/home/forms.py, 第53行,在clean方法中
- 引发位置:home.views.MemberDetailView
- Python可执行文件:../projDir/django-terp-venv/bin/python
- Python版本:3.8.2
英文:
Goal
I need to make the user accessible to a form for validation (i.e., through self.request.user)
Approach Taken
I use the get_form_kwargs() function in my view to make the request available to my form (as suggested here: Very similar problem).
Problem
Despite following the steps outlined in the StackOverflow answer linked above, I'm getting the error 'NoneType' object has no attribute 'user'
Code
views.py
class MemberDetailView(LoginRequiredMixin, generic.DetailView):
"""View class for member profile page"""
model = User
context_object_name = 'user'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs,)
context['form'] = JoinCommunityForm()
return context
class JoinCommunityFormView(FormView):
"""View class for join code for users to join communities"""
form_class = JoinCommunityForm
def get_form_kwargs(self, *args, **kwargs):
form_kwargs = super().get_form_kwargs(*args, **kwargs)
form_kwargs['request'] = self.request
return form_kwargs
def post(self, request, *args, **kwargs):
"""posts the request to join community only if user is logged in"""
if not request.user.is_authenticated:
return HttpResponseForbidden
return super().post(request, *args, **kwargs)
class MemberDashboardView(View):
"""View class to bring together the MemberDetailView and the JoinCommunityFormView"""
def get(self, request, *args, **kwargs):
view = MemberDetailView.as_view()
return view(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
view = JoinCommunityFormView.as_view()
form = JoinCommunityForm(request.POST)
if form.is_valid():
forms.py
*Line where error is being raised is user=self.request.user *
from communities.models import Community, UserRoleWithinCommunity
class JoinCommunityForm(forms.Form):
pin = forms.RegexField(
'^[A-HJ-NP-Z1-9]$',
max_length=6,
label='',
widget=forms.TextInput(attrs={
'oninput': 'this.value = this.value.toUpperCase()'
}
)
)
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(JoinCommunityForm, self).__init__(*args, **kwargs)
def clean(self):
cleaned_data = super().clean()
pin = self.cleaned_data['pin']
test_join_code = f'{pin}'
if Community.objects.filter(join_code=test_join_code).exists():
# Make sure user is not already a member of the community
communityToJoin = Community.objects.get(join_code=test_join_code)
if UserRoleWithinCommunity.objects.filter(
community=communityToJoin,
user=self.request.user
).exists():
raise forms.ValidationError("You're already a member
of that Community")
else:
return cleaned_data
else:
raise forms.ValidationError("Join code is incorrect")
Edit 1:
I've tried the approach suggested by Willem but am still butting up against the same error 'NoneType' object has no attribute 'user', which is tracing back to that one line in my form where I need to access the user through self.request.user.
All forms.py code remains the exact same as above. My views.py code has changed as follows (I now only have a single view rather than two views (form view and detail view) that feed into another view):
views.py
# all necessary import statements
class MemberDetailView(
LoginRequiredMixin, FormMixin,
generic.DetailView, ProcessFormView
):
"""View class for member profile page"""
model = User
context_object_name = 'user'
form_class = JoinCommunityForm
success_url = '#communities-card'
template_name = 'home/terpuser_detail.html'
def get_queryset(self, *args, **kwargs):
qs = super().get_queryset(*args, **kwargs)
if not self.request.user.is_superuser:
qs = qs.filter(pk=self.request.user.pk)
return qs
def get_context_data(self, **kwargs):
context = super().get_context_data(
**kwargs,
demo=get_object_or_404(Scenario.objects, scenario_id='demo'),
today=date.today(),
)
return context
def form_valid(self, form):
# this is where I put my code to create a new Community
# membership object
return super().form_valid(self, form)
urls.py
from django.urls import path
from . import views
urlpatterns = [
path('member/<slug:slug>/', views.MemberDetailView.as_view(), name='member-detail')
]
error message
- Request Method: POST
- Request URL: http://127.0.0.1:8000/member/tron/
- Django Version: 4.1.5
- Exception Type: AttributeError
- Exception Value: 'NoneType' object has no attribute 'user'
- Exception Location: ../projDir/home/forms.py, line 53, in clean
- Raised during: home.views.MemberDetailView
- Python Executable: ../projDir/django-terp-venv/bin/python
- Python Version: 3.8.2
答案1
得分: 1
以下是翻译好的部分:
这没有起作用的原因是因为您使用 JoinCommunityForm(request.POST) 构建了表单,而 Django 的 FormMixin 将调用 get_form_kwargs() 然后将这些参数传递给表单,正如我们在 源代码 [GitHub] 中所看到的:
def get_form(self, form_class=None):
"""返回在此视图中要使用的表单的实例。"""
if form_class is None:
form_class = self.get_form_class()
return form_class(**self.get_form_kwargs())
在您的视图中,您可以使用以下方法轻松处理这个问题:
from django.views.generic.edit import FormMixin, ProcessFormView
class MemberDetailView(
LoginRequiredMixin, FormMixin, generic.DetailView, ProcessFormView
):
model = User
context_object_name = 'user'
form_class = JoinCommunityForm
template_name = '...' # 在 MemberDetailView 中使用的模板
def form_valid(self, form):
# 当表单有效时执行某些操作
return super().form_valid(form)
英文:
The reason this did not work is because you constructed the form with JoinCommunityForm(request.POST), whereas Django's FormMixin will call get_form_kwargs() and then pass these to the form, as we can see in the source code [GitHub]:
> <pre><code>def get_form(self, form_class=None):
> """Return an instance of the form to be used in this view."""
> if form_class is None:
> form_class = self.get_form_class()
> return <b>form_class(**self.get_form_kwargs())</b></code></pre>
In your view you can however easily handle this with:
<pre><code>from django.views.generic.edit import FormMixin, ProcessFormView
class MemberDetailView(
LoginRequiredMixin, <b>FormMixin</b>, generic.DetailView, <b>ProcessFormView</b>
):
model = User
context_object_name = 'user'
form_class = JoinCommunityForm
template_name = '…' # template used in MemberDetailView
def form_valid(self, form):
# do something when the form is valid
return super().form_valid(self, form)</code></pre>
答案2
得分: 0
以下是翻译好的部分:
看起来我需要在表单类的 `__init__` 方法中获取当前用户(而不仅仅是 `request`)。下面是最终有效的 `views.py` 和 `forms.py` 代码:
## views.py ##
```python
# 所有必要的导入语句
class MemberDetailView(
LoginRequiredMixin, FormMixin,
generic.DetailView, ProcessFormView
):
"""成员个人资料页面的视图类"""
model = User
context_object_name = 'user'
form_class = JoinCommunityForm
success_url = '#communities-card'
template_name = 'home/terpuser_detail.html'
def get_queryset(self, *args, **kwargs):
qs = super().get_queryset(*args, **kwargs)
if not self.request.user.is_superuser:
qs = qs.filter(pk=self.request.user.pk)
return qs
def get_context_data(self, **kwargs):
context = super().get_context_data(
**kwargs,
demo=get_object_or_404(Scenario.objects, scenario_id='demo'),
today=date.today(),
)
return context
def form_valid(self, form):
# 这是我放置创建新的社区成员对象的代码的地方
return super().form_valid(self, form)
forms.py
解决我的问题的代码行位于表单类的 __init__ 方法中:self.user = self.request.user。然后,在表单类的 clean 方法中调用 self.user 而不是 self.request.user。
from communities.models import Community, UserRoleWithinCommunity
class JoinCommunityForm(forms.Form):
pin = forms.RegexField(
'^[A-HJ-NP-Z1-9]$',
max_length=6,
label='',
widget=forms.TextInput(attrs={
'oninput': 'this.value = this.value.toUpperCase()'
}
)
)
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None) # 将 request kwarg 实例化,如Willem建议的
self.user = self.request.user # 获取用户,这是解决错误的关键
super(JoinCommunityForm, self).__init__(*args, **kwargs)
def clean(self):
cleaned_data = super().clean()
pin = self.cleaned_data['pin']
test_join_code = f'{pin}'
if Community.objects.filter(join_code=test_join_code).exists():
# 确保用户不是该社区的成员
communityToJoin = Community.objects.get(join_code=test_join_code)
if UserRoleWithinCommunity.objects.filter(
community=communityToJoin,
user=self.user # 这里我调用 self.user 而不是 self.request.user
).exists():
raise forms.ValidationError("您已经是该社区的成员")
else:
return cleaned_data
else:
raise forms.ValidationError("加入代码不正确")
请注意,以上代码中的注释已被保留在翻译中。
<details>
<summary>英文:</summary>
It appears I needed to get the current user (not just the `request`) using the form class's `__init__` method. Below is the `views.py` and `forms.py` code that ended up working:
## views.py ##
all necessary import statements
class MemberDetailView(
LoginRequiredMixin, FormMixin,
generic.DetailView, ProcessFormView
):
"""View class for member profile page"""
model = User
context_object_name = 'user'
form_class = JoinCommunityForm
success_url = '#communities-card'
template_name = 'home/terpuser_detail.html'
def get_queryset(self, *args, **kwargs):
qs = super().get_queryset(*args, **kwargs)
if not self.request.user.is_superuser:
qs = qs.filter(pk=self.request.user.pk)
return qs
def get_context_data(self, **kwargs):
context = super().get_context_data(
**kwargs,
demo=get_object_or_404(Scenario.objects, scenario_id='demo'),
today=date.today(),
)
return context
def form_valid(self, form):
# this is where I put my code to create a new Community membership object
return super().form_valid(self, form)
## forms.py ##
The line that ended up solving my problems is in the form class's `__init__` method: `self.user = self.request.user`. Then, I call `self.user` in the form class's `clean` method rather than `self.request.user`
from communities.models import Community, UserRoleWithinCommunity
class JoinCommunityForm(forms.Form):
pin = forms.RegexField(
'^[A-HJ-NP-Z1-9]$',
max_length=6,
label='',
widget=forms.TextInput(attrs={
'oninput': 'this.value = this.value.toUpperCase()'
}
)
)
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None) # instantiate request kwarg as Willem suggests
self.user = self.request.user # get user, this was key to resolving error
super(JoinCommunityForm, self).__init__(*args, **kwargs)
def clean(self):
cleaned_data = super().clean()
pin = self.cleaned_data['pin']
test_join_code = f'{pin}'
if Community.objects.filter(join_code=test_join_code).exists():
# Make sure user is not already a member of the community
communityToJoin = Community.objects.get(join_code=test_join_code)
if UserRoleWithinCommunity.objects.filter(
community=communityToJoin,
user=self.user # here I call self.user instead of self.request.user
).exists():
raise forms.ValidationError("You're already a member
of that Community")
else:
return cleaned_data
else:
raise forms.ValidationError("Join code is incorrect")
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论