“'str' 对象没有属性 'get'” 将 slug 传递给 django 表单时
"'str' object has no attribute 'get'" when passing slug to django form
我正在尝试将 slug 传递给表单,以便它可以将用户帐户与相关组(在此 project/context 中称为 'events')相匹配。 slug 是事件的标识符,它有多种类型的多对多连接到用户配置文件(标准 django 用户模型的扩展,专门用于将用户连接到事件)。
该表单应该创建两个下拉选项字段,让事件管理者设置任何其他用户在事件中拥有的权限,即作为被动观察者、编辑者或管理者。但是,截至目前,django 生成错误
'str' object has no attribute 'get'
渲染页面时。异常位于函数value_from_datadict中的django/forms/widgets.py,报错在网上基本模板中的 0 base.dashboard.html,在呈现期间出现。
我设法通过删除两个字段但保留 slug 以及删除候选字段和 slug 而保留权限字段完好无损来避免错误。我还尝试将传递的值显式转换为表单中的 slug(以防某些奇怪的动态类型使系统崩溃),但没有效果。
我错过了什么?
我正在使用 Python 3.9、Django 3.2.5 和 django-crispy-forms 1.13
views.py
@login_required
def members(request, slug):
current_event = Event.objects.get(slug=slug)
member_query = (Q(is_observer=current_event) | Q(is_editor=current_event) | Q(is_manager=current_event))
current_event_members = Profile.objects.filter(member_query)
if request.method == 'POST':
form = EventMemberForm(request.POST, current_event.slug)
if form.is_valid():
if form.membership_level and form.candidate:
target = Profile.objects.get(user__username=form.candidate)
if form.membership_level == "observer":
target.is_observer.add(current_event)
target.is_editor.remove(current_event)
target.is_manager.remove(current_event)
if form.membership_level == "editor":
target.is_observer.remove(current_event)
target.is_editor.add(current_event)
target.is_manager.remove(current_event)
if form.membership_level == "manager":
target.is_observer.remove(current_event)
target.is_editor.remove(current_event)
target.is_manager.add(current_event)
if form.membership_level == "remove":
target.is_observer.remove(current_event)
target.is_editor.remove(current_event)
target.is_manager.remove(current_event)
return HttpResponseRedirect("/e/list")
else:
form = EventMemberForm(current_event.slug)
args = {}
args.update({"event": current_event})
args.update({"event_members": current_event_members})
args.update({"form": form})
return render(request, "management/event_members.html", args)
forms.py
class EventMemberForm(forms.Form):
MEMBERSHIP_LEVELS = [
('observer', 'Observer'),
('editor', 'Editor'),
('manager', 'Manager')
]
membership_level = forms.ChoiceField(label='Permissions:', choices=MEMBERSHIP_LEVELS, required=False)
candidate = forms.ModelChoiceField(queryset=Profile.objects.none(), label="Candidate:", required=False)
class Meta:
fields = ['membership_level', 'candidate']
def __init__(self, *args, **kwargs):
super(EventMemberForm, self).__init__(*args, **kwargs)
selected_event = Event.objects.get(slug=args[0])
self.fields['candidate'].queryset = Profile.objects.filter((~Q(is_observer=selected_event) | ~Q(is_editor=selected_event)) | ~Q(is_manager=selected_event) & Q(user__is_active=1)).order_by("user__username")
模板 (event_members.html)
{% extends "base.dashboard.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Members in Event "{{ event.name }}"</h1>
<div class="btn-toolbar mb-2 mb-md-0">
<div class="btn-group me-2">
<a href="{% url 'Event_List' %}" class="btn btn-sm btn-outline-secondary">List of Events</a>
</div>
</div>
</div>
<h2>Members</h2>
{% if event_members|length > 0 %}
{% if request.user.profile.is_manager %}
{% if form.non_field_errors %}
<div class="alert alert-danger">
<a class="close" data-dismiss="alert">×</a>
{% for non_field_error in form.non_field_errors %}
{{ non_field_error }}
{% endfor %}
</div>
{% endif %}
<form role="form" action="{% url 'Event_Members' slug=event.slug %}" method="post">
{% csrf_token %}
<div class="form-group">
{{ form|crispy }}
<button type="submit" class="btn btn-sm btn-outline-secondary">Change permissions</button>
</div>
</form>
{% endif %}
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Permissions</th>
</tr>
</thead>
<tbody>
{% for member in event_members %}
<tr>
<td><strong>{{ member.user.username }}</strong></td>
<td>
{% if member.is_manager %}
Manager
{% elif member.is_editor %}
Editor
{% elif member.is_observer %}
Observer
{% else %}
Error
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="alert alert-danger">Error: No members found. Contact an administrator!</div>
{% endif %}
{% endblock %}
models.py(个人资料)
class Profile(models.Model):
user = models.OneToOneField(User, related_name="profile", on_delete=models.CASCADE)
is_observer = models.ManyToManyField(Event, related_name="event_observer", blank=True)
is_editor = models.ManyToManyField(Event, related_name="event_editor", blank=True)
is_manager = models.ManyToManyField(Event, related_name="event_manager", blank=True)
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
models.py(事件)
class Event(models.Model):
name = models.CharField(verbose_name="Name", max_length=50)
date = models.DateField(verbose_name="Starting Date (DD.MM.YYYY)")
description = models.TextField(verbose_name="Description")
slug = models.SlugField(max_length=10)
def __str__(self):
return str(self.name)
def save(self, *args, **kwargs):
if not self.id: # skip if event already exists
if get_or_none(Event, slug=slugify(self.name[:10])) is None: # check for duplicate slugs
self.slug = slugify(self.name[:10])
else: # iteratively append integer to slug candidates until an unused slug is found
i = 0
finished = False
while not finished:
slug_candidate = slugify((self.name[:10-len(str(i))] + str(i)))
if get_or_none(Event, slug=slug_candidate) is None:
self.slug = slug_candidate
finished = True
else:
i = i + 1
if get_or_none(Event, name=self.name): # check for duplicate name and generate new one if needed (compare above)
i = 0
finished = False
while not finished:
name_candidate = str(self.name[:50 - len(str(i))] + str(i))
if get_or_none(Event, name=name_candidate) is None:
self.name = name_candidate
finished = True
else:
i = i + 1
super(Event, self).save(*args, **kwargs)
在您的 EventMemberForm __init__() 中调用 super.__init__() 并传递所有参数:
class EventMemberForm(forms.Form):
...
def __init__(self, *args, **kwargs):
super(EventMemberForm, self).__init__(*args, **kwargs)
BaseForm 来自 https://github.com/django/django/blob/main/django/forms:/forms.py
Class BaseForm():
....
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
initial=None, error_class=ErrorList, label_suffix=None,
empty_permitted=False, field_order=None, use_required_attribute=None, renderer=None):
在您的情况下,super 将使用 current_event.slug 作为第一个位置参数调用,它转到 BaseForm Class 中的“数据”。这肯定不是你想要发生的。
更新:最好使用关键字参数,这样您就可以在调用 super.__init__
之前轻松地从 kwargs 中删除 slug
问题源于使用自定义参数重载 form
。这必须在调用 super
函数之前从 kwargs
删除。那是因为来自父 class 的 init
-函数不知道这个额外的参数。
view.py
form = EventMemberForm(request.POST, slug=current_event.slug)
forms.py
class EventMemberForm(forms.Form):
...
def __init__(self, *args, **kwargs):
slug = kwargs.pop('slug', None)
super(EventMemberForm, self).__init__(*args, **kwargs)
selected_event = Event.objects.get(slug)
self.fields['candidate'].queryset = Profile.objects.filter((~Q(is_observer=selected_event) | ~Q(is_editor=selected_event)) | ~Q(is_manager=selected_event) & Q(user__is_active=1)).order_by("user__username")
我正在尝试将 slug 传递给表单,以便它可以将用户帐户与相关组(在此 project/context 中称为 'events')相匹配。 slug 是事件的标识符,它有多种类型的多对多连接到用户配置文件(标准 django 用户模型的扩展,专门用于将用户连接到事件)。
该表单应该创建两个下拉选项字段,让事件管理者设置任何其他用户在事件中拥有的权限,即作为被动观察者、编辑者或管理者。但是,截至目前,django 生成错误
'str' object has no attribute 'get'
渲染页面时。异常位于函数value_from_datadict中的django/forms/widgets.py,报错在网上基本模板中的 0 base.dashboard.html,在呈现期间出现。
我设法通过删除两个字段但保留 slug 以及删除候选字段和 slug 而保留权限字段完好无损来避免错误。我还尝试将传递的值显式转换为表单中的 slug(以防某些奇怪的动态类型使系统崩溃),但没有效果。
我错过了什么?
我正在使用 Python 3.9、Django 3.2.5 和 django-crispy-forms 1.13
views.py
@login_required
def members(request, slug):
current_event = Event.objects.get(slug=slug)
member_query = (Q(is_observer=current_event) | Q(is_editor=current_event) | Q(is_manager=current_event))
current_event_members = Profile.objects.filter(member_query)
if request.method == 'POST':
form = EventMemberForm(request.POST, current_event.slug)
if form.is_valid():
if form.membership_level and form.candidate:
target = Profile.objects.get(user__username=form.candidate)
if form.membership_level == "observer":
target.is_observer.add(current_event)
target.is_editor.remove(current_event)
target.is_manager.remove(current_event)
if form.membership_level == "editor":
target.is_observer.remove(current_event)
target.is_editor.add(current_event)
target.is_manager.remove(current_event)
if form.membership_level == "manager":
target.is_observer.remove(current_event)
target.is_editor.remove(current_event)
target.is_manager.add(current_event)
if form.membership_level == "remove":
target.is_observer.remove(current_event)
target.is_editor.remove(current_event)
target.is_manager.remove(current_event)
return HttpResponseRedirect("/e/list")
else:
form = EventMemberForm(current_event.slug)
args = {}
args.update({"event": current_event})
args.update({"event_members": current_event_members})
args.update({"form": form})
return render(request, "management/event_members.html", args)
forms.py
class EventMemberForm(forms.Form):
MEMBERSHIP_LEVELS = [
('observer', 'Observer'),
('editor', 'Editor'),
('manager', 'Manager')
]
membership_level = forms.ChoiceField(label='Permissions:', choices=MEMBERSHIP_LEVELS, required=False)
candidate = forms.ModelChoiceField(queryset=Profile.objects.none(), label="Candidate:", required=False)
class Meta:
fields = ['membership_level', 'candidate']
def __init__(self, *args, **kwargs):
super(EventMemberForm, self).__init__(*args, **kwargs)
selected_event = Event.objects.get(slug=args[0])
self.fields['candidate'].queryset = Profile.objects.filter((~Q(is_observer=selected_event) | ~Q(is_editor=selected_event)) | ~Q(is_manager=selected_event) & Q(user__is_active=1)).order_by("user__username")
模板 (event_members.html)
{% extends "base.dashboard.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Members in Event "{{ event.name }}"</h1>
<div class="btn-toolbar mb-2 mb-md-0">
<div class="btn-group me-2">
<a href="{% url 'Event_List' %}" class="btn btn-sm btn-outline-secondary">List of Events</a>
</div>
</div>
</div>
<h2>Members</h2>
{% if event_members|length > 0 %}
{% if request.user.profile.is_manager %}
{% if form.non_field_errors %}
<div class="alert alert-danger">
<a class="close" data-dismiss="alert">×</a>
{% for non_field_error in form.non_field_errors %}
{{ non_field_error }}
{% endfor %}
</div>
{% endif %}
<form role="form" action="{% url 'Event_Members' slug=event.slug %}" method="post">
{% csrf_token %}
<div class="form-group">
{{ form|crispy }}
<button type="submit" class="btn btn-sm btn-outline-secondary">Change permissions</button>
</div>
</form>
{% endif %}
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Permissions</th>
</tr>
</thead>
<tbody>
{% for member in event_members %}
<tr>
<td><strong>{{ member.user.username }}</strong></td>
<td>
{% if member.is_manager %}
Manager
{% elif member.is_editor %}
Editor
{% elif member.is_observer %}
Observer
{% else %}
Error
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="alert alert-danger">Error: No members found. Contact an administrator!</div>
{% endif %}
{% endblock %}
models.py(个人资料)
class Profile(models.Model):
user = models.OneToOneField(User, related_name="profile", on_delete=models.CASCADE)
is_observer = models.ManyToManyField(Event, related_name="event_observer", blank=True)
is_editor = models.ManyToManyField(Event, related_name="event_editor", blank=True)
is_manager = models.ManyToManyField(Event, related_name="event_manager", blank=True)
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
models.py(事件)
class Event(models.Model):
name = models.CharField(verbose_name="Name", max_length=50)
date = models.DateField(verbose_name="Starting Date (DD.MM.YYYY)")
description = models.TextField(verbose_name="Description")
slug = models.SlugField(max_length=10)
def __str__(self):
return str(self.name)
def save(self, *args, **kwargs):
if not self.id: # skip if event already exists
if get_or_none(Event, slug=slugify(self.name[:10])) is None: # check for duplicate slugs
self.slug = slugify(self.name[:10])
else: # iteratively append integer to slug candidates until an unused slug is found
i = 0
finished = False
while not finished:
slug_candidate = slugify((self.name[:10-len(str(i))] + str(i)))
if get_or_none(Event, slug=slug_candidate) is None:
self.slug = slug_candidate
finished = True
else:
i = i + 1
if get_or_none(Event, name=self.name): # check for duplicate name and generate new one if needed (compare above)
i = 0
finished = False
while not finished:
name_candidate = str(self.name[:50 - len(str(i))] + str(i))
if get_or_none(Event, name=name_candidate) is None:
self.name = name_candidate
finished = True
else:
i = i + 1
super(Event, self).save(*args, **kwargs)
在您的 EventMemberForm __init__() 中调用 super.__init__() 并传递所有参数:
class EventMemberForm(forms.Form):
...
def __init__(self, *args, **kwargs):
super(EventMemberForm, self).__init__(*args, **kwargs)
BaseForm 来自 https://github.com/django/django/blob/main/django/forms:/forms.py
Class BaseForm():
....
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
initial=None, error_class=ErrorList, label_suffix=None,
empty_permitted=False, field_order=None, use_required_attribute=None, renderer=None):
在您的情况下,super 将使用 current_event.slug 作为第一个位置参数调用,它转到 BaseForm Class 中的“数据”。这肯定不是你想要发生的。
更新:最好使用关键字参数,这样您就可以在调用 super.__init__
之前轻松地从 kwargs 中删除 slug问题源于使用自定义参数重载 form
。这必须在调用 super
函数之前从 kwargs
删除。那是因为来自父 class 的 init
-函数不知道这个额外的参数。
view.py
form = EventMemberForm(request.POST, slug=current_event.slug)
forms.py
class EventMemberForm(forms.Form):
...
def __init__(self, *args, **kwargs):
slug = kwargs.pop('slug', None)
super(EventMemberForm, self).__init__(*args, **kwargs)
selected_event = Event.objects.get(slug)
self.fields['candidate'].queryset = Profile.objects.filter((~Q(is_observer=selected_event) | ~Q(is_editor=selected_event)) | ~Q(is_manager=selected_event) & Q(user__is_active=1)).order_by("user__username")