英文:
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>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论