【问题标题】:Django Autocomplete Light create new choiceDjango Autocomplete Light 创造新选择
【发布时间】:2018-11-17 16:29:17
【问题描述】:

我一直在学习为 Django Autocomplete Light 提供的以下教程:

https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html

我已成功为我的表单中的一个字段实现了自动完成功能,但是我无法完成以下部分:

https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html#creation-of-new-choices-in-the-autocomplete-form

文档指出,我应该能够添加一个功能,如果用户所需的选择不可用,该功能允许用户在表单中创建新选择。但是本教程并没有特别清楚地解释如何执行此操作。

我正在尝试实现一个表单,用户可以通过以下方式创建新的反馈:

  1. 从自动完成的类别列表中选择
  2. 选择与所选类别对应的消息
  3. 如果他们希望选择的类别或消息不可用,他们应该能够添加到现有选择中

我已经部分实现了这个,但它似乎无法正常工作,就像没有选择类别一样,消息的下拉列表显示类别列表。但是,如果选择了类别,则会根据需要显示正确的消息。

models.py

class Feedback(models.Model):
     feedback_id = models.IntegerField(primary_key=True,default=0)
     pre_defined_message = models.ForeignKey('Message',on_delete=models.CASCADE,null=True,blank=True) # Selected from a pre defined list depending on selected category
     points = models.IntegerField(default=0)
     lecturer = models.ForeignKey('LecturerProfile', on_delete=models.CASCADE, null=True, blank=True)
     student = models.ForeignKey('StudentProfile', on_delete=models.CASCADE, null=True, blank=True)
     which_course = models.ForeignKey('Course', on_delete=models.CASCADE, null=True, blank=True)
     datetime_given = models.DateTimeField(default=timezone.now, blank=False)
     optional_message = models.CharField(max_length=200,default="")
     category = models.ForeignKey('Category', on_delete=models.CASCADE, null=True, blank=True)

 class Category(models.Model):
     name = models.CharField(max_length=20, default="Empty",primary_key=True)

     def __str__(self):
         return self.name

class Message(models.Model):
     category = models.ForeignKey('Category',on_delete=models.CASCADE,null=True,blank=True)
     text = models.CharField(max_length=200,default="No message",primary_key=True)

     def __str__(self):
          return self.text

forms.py

class FeedbackForm(autocomplete.FutureModelForm):
     optional_message = forms.CharField(max_length=200, required=False)

     class Meta:
         model = Feedback
         fields = ('category', 'pre_defined_message','optional_message','points')
         widgets = {
             'pre_defined_message': autocomplete.ModelSelect2(url='category_autocomplete',forward=['category']),
             'category': autocomplete.ModelSelect2(url='category_autocomplete')
         }
         help_texts = {
             'pre_defined_message': "Select a Message",
             'category': 'Category',
             'optional_message': "Optional Message",
             'points': "Points"
         }

views.py

class CategoryAutocomplete(autocomplete.Select2QuerySetView):
     def get_queryset(self):
         if not self.request.user.is_authenticated or not self.request.user.is_lecturer:
             return Category.objects.none()

         query_set = Category.objects.all()

         category = self.forwarded.get('category', None)

         if self.q:
             query_set = query_set.filter(name__istartswith=self.q)
             return query_set

         if category:
             query_set = Message.objects.filter(category=category)

         return query_set

urls.py

re_path(r'^category-autocomplete/$', CategoryAutocomplete.as_view(create_field='name'), name='category_autocomplete'),


我已经寻找了一段时间的答案,并且一直在努力寻找解决方案。 我也知道我的 forms.py 可能没有最有效/最干净的代码,并且愿意接受改进这一点的建议。我已经尝试定义一个 init 方法,但是我无法成功地做到这一点。

提前致谢

【问题讨论】:

    标签: python django django-forms django-autocomplete-light


    【解决方案1】:

    在搜索完 Django Autocomplete Light 的所有开源文档后:

    https://github.com/yourlabs/django-autocomplete-light

    我相信我已经找到了解决方案,并认为我应该将它分享给对所提供的教程感到困惑的其他人。

    在达到我上面的阶段(即工作自动完成)后,您必须包含一个 get_create_option 方法,以允许视图在检索 create_field 时了解要做什么。

    所以在 urls.py 的 urlpatterns 列表中确保存在以下行:

    re_path(r'^category-autocomplete/$', CategoryAutocomplete.as_view(model=Category,create_field='name'), name='category_autocomplete')
    


    注意:create_field 变量必须设置为相关模型的主键。在我的例子中,Category 模型的主键是name

    教程中没有说清楚的是下一步。查看以下文件后:

    https://github.com/yourlabs/django-autocomplete-light/blob/master/src/dal_select2/views.py

    我找到了一个方法 get_create_option 来处理新选项的创建。

    def get_create_option(self, context, q):
        """Form the correct create_option to append to results."""
        create_option = []
        display_create_option = False
        if self.create_field and q:
            page_obj = context.get('page_obj', None)
            if page_obj is None or page_obj.number == 1:
                display_create_option = True
    
            # Don't offer to create a new option if a
            # case-insensitive) identical one already exists
            existing_options = (self.get_result_label(result).lower()
                                for result in context['object_list'])
            if q.lower() in existing_options:
                display_create_option = False
    
        if display_create_option and self.has_add_permission(self.request):
            create_option = [{
                'id': q,
                'text': _('Create "%(new_value)s"') % {'new_value': q},
                'create_id': True,
            }]
        return create_option
    


    在我的 views.py 中的 CategoryAutocomplete 类中包含此方法后,在搜索中创建新类别的能力终于奏效了!

    我现在很难使用先前选择的 Category 作为外键创建 Message 对象,因为这也没有很好的文档记录。如果我找到解决方案,我会更新这个答案。

    希望这对某人有所帮助!

    更新

    虽然有点小技巧,但我还是设法设置了 Message 模型的外键。我只需访问创建的 Message 并在表单验证本身中设置其类别字段:

    if request.method == 'POST':
            form = FeedbackForm(request.POST)
            if form.is_valid():
                new_fb = form.save(commit=False)
                # When a new message is made, the category it is associated with is not saved
                # To fix this, set the category field within this form and save the message object.
                new_fb.pre_defined_message.category = Category.objects.get(name=new_fb.category)
                new_fb.pre_defined_message.save()
    

    【讨论】:

      【解决方案2】:

      也许问题是用户没有 add permission 那个 get_create_option checks

      如果你把它添加到你的视图中会起作用吗?

      def has_add_permission(self, request): return True

      【讨论】:

      • 你说得对,这是问题之一。然而主要问题是我没有意识到我必须包含 get_create_option 方法
      • 我不这么认为。
      【解决方案3】:

      我有模特

       class sites(models.Model): #Site Owner for standard sites will be system_1
          site = models.CharField(max_length=100)
          site_owner = models.ForeignKey(User, on_delete=models.CASCADE, blank = True, null=True)
          def __str__(self):
              return self.site
      

      我希望用户能够通过自动完成添加新网站并记录哪个用户创建了网站

      在 dal\views.py - 类 BaseQuerySetView(ViewMixin, BaseListView): 有以下

      def create_object(self, text):
          """Create an object given a text."""
          return self.get_queryset().get_or_create(
              **{self.create_field: text,})[0]
      

      所以我在我的视图中的自动完成类中覆盖了这个

      def create_object(self, text):
          """Create an object given a text."""        
          return self.get_queryset().get_or_create(site_owner=self.request.user,
              site=text)[0]
      

      如果需要,这可以进一步扩展,然后更新模型中的多行,在您的情况下,您应该能够将先前选择的类别传递到此定义中。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-03-29
        • 2018-02-17
        • 1970-01-01
        • 1970-01-01
        • 2018-09-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多