首页 > 解决方案 > 如何将两个查询混合为一个作为下拉元素

问题描述

我需要加入并传递类别和子类别的两个查询作为自动完成 select2 下拉列表的元素,作为我的 django 表单字段,如下所示:

在此处输入图像描述

这是我的表格:

class CategoriesAutocomplete(autocomplete.Select2QuerySetView):
    def get_queryset(self):
       if not self.request.user.is_authenticated:
           return Categories.objects.none()
       qs = Categories.objects.all()
       if self.q:
           qs = qs.filter(Q(name__icontains=self.q))
       return qs

   def get_result_label(self, item):
       return format_html( item.name)


class categories_form(forms.ModelForm):
    categories = forms.ModelChoiceField(
        queryset= Categories.objects.none(),
        widget= autocomplete.ModelSelect2(
            url='load_categories',
            attrs={
                    'data-placeholder': 'Select a category',
                    'data-html': True,
                    'style': 'min-width: 15em !important;',
                }
            )
    )

    class Meta:
        model = Post
        fields = ['categories']

    def __init__(self, *args, **kwargs):
        super(category_form, self).__init__(*args, **kwargs)
        self.fields['categories'].queryset = Categories.objects.all()

在网址中:

path('ajax/categories-autocomplete', CategoriesAutocomplete.as_view(), name='load_categories'),

对于类别模型:

class Categories(models.Model):
    name = models.CharField(max_length=100)
    brief = models.TextField(null=True)
    slug = models.SlugField(max_length=200)
    date = models.DateTimeField(default=timezone.now)

    class Meta:
        db_table = "categories"

对于子类别模型:

class Sub_Categories(models.Model):
    name = models.CharField(max_length=100)
    brief = models.TextField(null=True)
    slug = models.SlugField(max_length=200)
    date = models.DateTimeField(default=timezone.now)

    class Meta:
        db_table = "sub_categories"

将类别与子类别联系起来的模型:

class Categories_Sub_Categories(models.Model):
    category = models.OneToOneField(Categories, primary_key=True, on_delete=models.CASCADE)
    sub_cat = models.OneToOneField(Sub_Categories, on_delete=models.CASCADE)

    class Meta:
        db_table = "categories_sub-categories"

在 Post 模型中:

class Post(models.Model):
    title = models.CharField(max_length=50)
    descript = HTMLField(blank=True, null=True)
    categories = models.ForeignKey(Categories, blank=True, null=True, on_delete=models.CASCADE)
    subcategories = models.ForeignKey(Sub_Categories, blank=True, null=True, on_delete=models.CASCADE)
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    date = models.DateTimeField(default=timezone.now)

更新:

@ha-neul 提供的答案只是有一个错误。我用一个例子来展示它:

这是下拉列表中的预期:

**Asia**
   China
   Malaysia
   India
   Tajikistan
   Iran
   Qatar

**Europe**
   Germany
   Italy
   Spain
   Netherlands
   France

**Africa**
  Gana
  ...

但这就是我所看到的:

    **Asia**
       China
       Malaysia
    
    **Europe**
       Netherlands
       France
       Sweden
       Norway

   **Asia**
     India
     Tajikistan
     Iran



  **Europe**
     Germany
     Italy

  **Asia**
    Qatar

  **Africa**
     Gana
     ...

 **America**
   ....

**Europe**
   Spain

在 SubCategory 表中,我有类似的内容:

 id ........... category_id
 1                1
 2                1
 3                1
 4                1
 5                3
 6                1
 7                2

我正在关注这个包。任何让我更接近解决方案的想法将不胜感激!

标签: django

解决方案


如果这是您想要实现的目标,那么:

在此处输入图像描述

简短的回答是你应该

  1. 让您的子类别ForeignKeyField参考类别模型。
  2. 使用Select2GroupQuerySetView而不是Select2QuerySetView.

但是实现起来有点复杂。

首先,虽然django- autocomplete -light 的源代码有. 因此,您必须在自己的. 另外,源码有错别字,需要修正。Select2GroupQuerySetViewautocomplete.Select2GroupQuerySetViewviews.py

步骤 1. 在 models.py 中:

class Category(models.Model):
    name = models.CharField(max_length=100)
    brief = models.TextField(null=True)
    slug = models.SlugField(max_length=200)
    date = models.DateTimeField(default=timezone.now)

class SubCategory(models.Model):

    ##################
    #You need add this line, so there is a one-to-many relationship

    category = models.ForeignKey(Category, on_delete=models.CASCADE,
        related_name='subcategories')

    ###############

    name = models.CharField(max_length=100)
    brief = models.TextField(null=True)
    slug = models.SlugField(max_length=200)
    date = models.DateTimeField(default=timezone.now)

Step2.in views.py 复制粘贴 Select2GroupQuerySetView 代码并修正错字

# import collections, so Select2GroupQuerySetView can work

import collections  

class Select2GroupQuerySetView(autocomplete.Select2QuerySetView):

    group_by_related = None
    related_field_name = 'name'

    def get_results(self, context):

        if not self.group_by_related:
            raise ImproperlyConfigured("Missing group_by_related.")

        groups = collections.OrderedDict()

        object_list = context['object_list']

        print(object_list)
        object_list = object_list.annotate(
            group_name=F(f'{self.group_by_related}__{self.related_field_name}'))

        for result in object_list:
            group_name = getattr(result, 'group_name')
            groups.setdefault(group_name, [])
            groups[group_name].append(result)

        return [{
            'id': None,
            'text': group,
            'children': [{
                'id': result.id,
                'text': getattr(result, self.related_field_name),

                # this is the line I had to comment out
                #'title': result.descricao 
            } for result in results]
        } for group, results in groups.items()]

3. 使用 Select2GroupQuerySetView 编写自己的视图

class SubCategoryAutocomplete(Select2GroupQuerySetView):
    print('under subcategory autocomplete')

    group_by_related = 'category'   # this is the fieldname of ForeignKey
    related_field_name = 'name'     # this is the fieldname that you want to show.

    def get_queryset(self):
        ##### Here is what you normally put... I am showing the minimum code. 

        qs = SubCategory.objects.all() 

        if self.q:
            qs = qs.filter(name__istartswith=self.q)

        return qs

如何在您的项目中使用此视图?

1.假设您有一个Post模型如下,子类别为 ForeignKey。

class Post(models.Model):
    title = models.CharField(max_length=100)
    
    subcategory = models.ForeignKey(SubCategory,on_delete=models.CASCADE)

    def __str__(self):
        return self.title

2.您将生成一个包含子类别自动完成字段的 PostForm。

在forms.py中

from django.conf.urls import url

class PostForm(forms.ModelForm):

    subcategory = forms.ModelChoiceField(
        queryset=Subcategory.objects.all(),

        widget=autocomplete.ModelSelect2(url='subcategory-autocomplete')
    )

    class Meta:
        model= Post
        fields = ('title','subcategory')

  1. 您将生成一个CreatePostView使用泛型CreateView
from django.views.generic.edit import CreateView

class CreatePostView(CreateView):
    model=Post
    template_name='yourapp/yourtemplate.html'# need to change

    form_class=PostForm
    success_url = '/'
  1. 现在,在您的 urls.py 中,一个CreatePostView用于自动完成视图的另一个 url。
urlpatterns = [
    url(
        r'^subcategory-autocomplete/$',
        SubCategoryAutocomplete.as_view(),
        name='subcategory-autocomplete',
    ),
    path('post/create',CreatePostView.as_view(), name='create_post'),
  1. 一切就绪,您将post/create看到一个带有子类别自动完成字段的 PostForm。

  2. OP 在使用上面的代码后有一个奇怪的分组行为。他在评论中提到:

在 qs 中添加了 .order_by(category_id')` 并修复了它。


推荐阅读