【问题标题】:"'str' object has no attribute 'get'" when passing slug to django form将slug传递给django表单时,“'str'对象没有属性'get'”
【发布时间】:2022-01-10 10:16:11
【问题描述】:

我正在尝试将 slug 传递给表单,以便它可以将用户帐户与相关组(在此项目/上下文中称为“事件”)匹配。 slug 是事件的标识符,它与用户配置文件有多种类型的多对多连接(标准 django 用户模型的扩展,专门用于将用户连接到事件)。

该表单应该创建两个下拉选择字段,以让事件管理员设置任何其他用户在事件中拥有的权限,即作为被动观察者、编辑者或管理员。但是,截至目前,django会产生错误

'str' 对象没有属性'get'

渲染页面时。异常位于函数 value_from_datadict 中的 django/forms/widgets.py 中,错误位于基本模板 base 的第 0 行。 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">&times;</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)

【问题讨论】:

    标签: python django django-forms


    【解决方案1】:

    问题源于使用自定义参数重载form。在调用 super 函数之前,必须将其从 kwargs删除。那是因为父类的init-function 不知道这个额外的参数。

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

    【讨论】:

    • 用 xxx.pop(args[0]) 删除 arg 不起作用,因为在代码中的另一个调用中,slug 作为第二个位置参数给出,你将删除错误的参数“form = EventMemberForm(request.POST, current_event.slug)”。我坚持认为,使用位置参数这样做不是一个好主意。
    • 对。代码示例优化。
    【解决方案2】:

    在 EventMemberForm __init__() 中调用 super.__init__() 并传递所有参数:

    class EventMemberForm(forms.Form):
        
    ...
    
        def __init__(self, *args, **kwargs):
            super(EventMemberForm, self).__init__(*args, **kwargs)
    
    

    来自https://github.com/django/django/blob/main/django/forms:/forms.py的BaseForm

    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 类中的“数据”。这肯定不是你想要发生的。

    更新:更好地使用关键字参数,以便您可以在调用 super.__init__ 之前轻松地从 kwargs 中删除 slug

    【讨论】:

    • 对不起,但这与位置参数无关。看看我的回答。
    • 对不起,我说的和你说的完全一样。由于在这种情况下参数是作为位置给出的,因此在调用 init 时没有错误消息,但后来在 init 内部,init 期望“数据”的另一个内容。我也提到过,它并不打算将该参数移交给 super.init。
    • 下次请提供代码示例,不要猜测提问者的意图。如果问题不确定,您可以为问题创建评论。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-01
    • 1970-01-01
    • 2014-04-28
    • 2017-04-25
    • 2020-12-11
    • 2014-10-13
    相关资源
    最近更新 更多