【问题标题】:How can I edit manytomanyfield data如何编辑多字段数据
【发布时间】:2022-01-19 17:54:48
【问题描述】:

我正在尝试编辑多字段数据。这是我的模型

class Permission(models.Model):
    shop = models.ForeignKey(Shop, on_delete=models.SET_NULL, null=True)
    permission_title = models.CharField(max_length=255)

    class Meta:
        ordering = ["-id"]
    
    def __str__(self):
        return self.permission_title



class Roles(models.Model):
    shop = models.ForeignKey(Shop, on_delete=models.SET_NULL, null=True)
    role_title = models.CharField(max_length=255)
    permissions = models.ManyToManyField(Permission)

    class Meta:
        ordering = ["-id"]
    
    def __str__(self):
        return self.role_title

在这些模型中,我有一个称为权限的模型。该模型与角色模型存在多域关系。我想编辑这个manytomanyfield。我已经完成了创作部分。但是现在我想编辑数据并且想在模板中显示数据,这是我在创建角色时选择的。我想显示一个输入类型 checkox

这是我的看法:-

def createRolesView(request, shop_id):
    shopId = get_object_or_404(Shop, pk=shop_id)
    permissions = Permission.objects.filter(
                shop=shopId.id
            )
    if shopId.user == request.user:
        if request.method == "POST":
            role_title = request.POST.get("role_title")
            shop = Shop.objects.get(id=shopId.id)
            permissions = request.POST.getlist("permissions")
            rl = Roles(
                role_title = role_title,
                shop = shop,
                
            )
            rl.save()
            for p in permissions:
                rl.permissions.add(p)

            rl.save()
            return redirect(f"/adminpanel/roles/{shopId.id}/")

        args = {
            "shopId": shopId,
            "permissions": permissions,
        }
        return render(request, "roles/create-role.html", args)
    else:
        return redirect("warning")


def editRolesView(request, role_id, shop_id):
    shopId = get_object_or_404(Shop, pk=shop_id)
    roleId = get_object_or_404(Roles, pk=role_id)

    if shopId.user == request.user:
        if request.method == "POST":
            roleId.role_title = request.POST.get("role_title")
            shop = Shop.objects.get(id=shopId.id)
            # roleId.permissions_set.all()
        args = {
            "shopId": shopId,
            "roleId": roleId,
        }
        return render(request, "roles/edit-role.html", args)
    else:
        return redirect("warning")

【问题讨论】:

  • 你已经尝试过什么?什么不工作?

标签: html django many-to-many


【解决方案1】:

仅供参考,我自己没有编写代码。

[编辑。这个问题引起了“痒”,所以我把它编码成符合我是对的。跳过一个非常通用的编辑 M2M 视图]

两个简单的表格

一个显示分配给角色的当前权限,它具有查询集roles.permissions.all(),以及每个“删除”复选框。

另一个显示可用权限,每个权限都有一个“添加”复选框。查询集可能是permissions.exclude( roles__pk=this_role.pk)(排除那些已经添加的,尽管添加已经添加的东西是无害的)。您还应该排除不允许此角色实例获取的任何权限。

您可能会使用 Django Formsets,但在这种情况下,处理来自 request.POST 的原始数据也相当容易。您的模板基本上是一个包含

实例的表单
<input type="checkbox" name="add" value="{{permission.pk}}" >

<input type="checkbox" name="remove" value="{{permission.pk}}" >

从权限实例中收集适当的描述性文本。

如果是我,我会将它们显示为左右表格,但只要它们位于带有提交(和完成?)按钮的 &lt;form&gt; 内,就没有关系。

在您的 POST 处理中,您将使用 request.POST.getlist('add')request.POST.getlist('remove') 获得检查过的 pk 值。对于每一个,重新验证它是否在呈现为表单的原始查询集中,以及 pk 值是否可以接受

role.permissions.add( permission)  # or .remove( permission)

(其中权限是重新验证的 Permissions 实例)

在提交上述内容后,您可能会再次重定向回相同的内容,以便用户可以看到请求的添加和删除已经发生。 “完成”按钮将重定向到别处(if "Done" in request.POST ...)

[编辑] 让我痒痒的结果我不得不抓挠......

class GenericEditM2MView( DetailView):
    #model = Model         # required as per DetailView
    # template_name =      # as per DetailView
    #m2m_fieldname = None # no longer required if unique: the name of the model's m2m field to operate on
    remove_qs = None     # defaults to .all() of the m2m field
    success_url = '.'    # defaults to displaying the modified m2m relationship unless done
    done_url = None      # where to go if submit="Done", defaults to success_url 

    """
    template_name must define a form full of checkboxes, obtained from
      {% for p in add_qs %}
       <input type="checkbox" name="add" value="{{p.pk}}" > {% endfor}
      {% for p in remove_qs %}
       <input type="checkbox" name="remove " value="{{p.pk}}" > {% endfor}

   default is to return to this same view after submit, to show that the changes
   have been made. You can supply <input type="submit" name="submit" value="done" />
   which will go to done_url instead of success_url

   example use:

    class PenStockM2MView( GenericEditM2MView):
        template_name = 'playpen/edit_m2m.html' 
        model = PenStock
        # m2m_fieldname = 'name' # works it out if ony one M2M field on the model
        done_url = '/playpen/OK'
    """

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        # everything works without this __init__ provided self.m2m_fieldname is present and correct.
        # if model has only one m2m field, locate it via _meta as default. 
        # Also check m2m_fieldname is m2m because very confusing errors later if it's not!

        f = getattr(self, 'm2m_fieldname', None)
        m2m_fieldnames = [ field.name for field in self.model._meta.get_fields() if field.many_to_many ]
        model_name = self.model.__name__

        if f and not f in m2m_fieldnames:
            raise AttributeError( f'field "{f}" is not a many-to-many field in {model_name}')

        if not f:
            if len( m2m_fieldnames ) == 1:
                self.m2m_fieldname = m2m_fieldnames[0]
            else:
                raise AttributeError( f'Cannot identify a unique many-to-many field in {model_name}' )      

    def get_add_queryset(self):
        field = getattr( self.object, self.m2m_fieldname)
        remove_qs = self.get_remove_queryset()
        already_there = remove_qs.values_list('pk', flat=True)
        return remove_qs.model.objects.exclude( pk__in = already_there)  # is qs.model documented?

    def get_remove_queryset(self):
        if hasattr( self, 'remove_queryset'):
            return self.remove_qs
        remove_qs = getattr( self.object, self.m2m_fieldname)
        return remove_qs.all()    

    def get_context_data( self, **kwargs):
        context = super().get_context_data( **kwargs)
        context['add_qs'] = self.get_add_queryset()
        context['remove_qs'] = self.get_remove_queryset()
        return context

    def post( self, request, *args, **kwargs):
        self.object = self.get_object()
        add = request.POST.getlist('add')
        remove = request.POST.getlist('remove')
        add_objs =    list( self.get_add_queryset().filter(pk__in=add) )
        remove_objs = list( self.get_remove_queryset().filter(pk__in=remove) )
        field = getattr( self.object, self.m2m_fieldname )
        field.add( *add_objs)
        field.remove( *remove_objs)
        return HttpResponseRedirect( self.get_done_url() or self.get_success_url() )

    def get_success_url(self):
       return self.success_url

    def get_done_url( self):
        done = self.request.POST.get("submit", None)
        if done == "done" and hasattr(self, 'done_url'):
            return self.done_url 
        return None 

这是一个模板(不完全通用。在 base.html 中使用 Bootstrap。)

<div class="row">

<div class="col-md-3 col-sm-4 col-xs-6">
    <h1> Remove </h1>
    <table class="table tbl">
    {% for p in remove_qs %}
      <tr><td>{{p.name }}</td><td><input type="checkbox" name="remove" value="{{p.pk}}" form="the-form" class="BigCheckbox"></td></tr>
    {%endfor %}
    </table>
</div>

<div class="col-md-3 col-sm-4 col-xs-6">
    <h1> Add </h1>
    <table class="table tbl">
    {% for p in add_qs %}
      <tr><td>{{p.name }}</td><td><input type="checkbox" name="add" value="{{p.pk}}" form="the-form" class="BigCheckbox"></td></tr>
    {%endfor %}
    </table>
</div>

</div>

<div class="row">
 <form method="post" id="the-form">    {% csrf_token %}
 <input type="submit" name="submit" value="submit" />
 &emsp;
 <input type="submit" name="submit" value="done" />
</form>
</div>

【讨论】:

    猜你喜欢
    • 2021-08-23
    • 2015-02-12
    • 1970-01-01
    • 2011-02-13
    • 1970-01-01
    • 1970-01-01
    • 2021-10-28
    • 1970-01-01
    • 2019-11-09
    相关资源
    最近更新 更多