Django Autocomplete Light создаст новый выбор - PullRequest
0 голосов
/ 17 ноября 2018

Я работал над следующим учебным пособием для автозаполнения Django Light:

https://django -autocomplete-light.readthedocs.io / о / ведущий / tutorial.html

Я успешно реализовал автозаполнение для одного из полей в моей форме, однако мне не удалось заполнить следующий раздел:

https://django -autocomplete-light.readthedocs.io / о / мастер / tutorial.html # создания-оф-нового-выбор-в-автозаполнения-форма

В документации говорится, что я должен иметь возможность добавить функцию, которая позволяет пользователю создавать новый выбор в форме, если его требуемый выбор недоступен. Однако в руководстве не совсем ясно, как объяснить, как это сделать.

Я пытаюсь реализовать форму, в которой пользователь может создать новый отзыв по:

  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 , но не смог сделать это успешно.

Заранее спасибо

Ответы [ 2 ]

0 голосов
/ 20 ноября 2018

Может быть, проблема в том, что у пользователя нет разрешения на добавление , который get_create_option проверяет ?

Работает ли это, если вы добавите это к своему представлению?

def has_add_permission(self, request): return True

0 голосов
/ 18 ноября 2018

После поиска по всем документам с открытым исходным кодом Django Autocomplete Light:

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

Я считаю, что нашел решение этой проблемы и подумал, что должен поделиться им с другими, смущеннымипредоставленный учебник.

После достижения стадии, которая у меня есть выше (т. е. работает автозаполнение), вы должны включить метод get_create_option, чтобы позволить представлению понять, что делать при получении поля create_field.

Поэтому в списке urlpatterns в urls.py убедитесь, что присутствует следующая строка:

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


После включения этого метода в мой класс CategoryAutocomplete в моем views.py, возможность создания новой категории в поиске наконец-то сработала!

У меня возникли трудностисоздание объекта сообщения с ранее выбранной категорией в качестве внешнего ключа, так как это также недостаточно хорошо документировано.Я обновлю этот ответ, если найду решение.

Надеюсь, это кому-нибудь поможет!

ОБНОВЛЕНИЕ

Хотя это немноговзлома, мне удалось установить внешний ключ модели сообщения.Я просто получаю доступ к созданному сообщению и устанавливаю его поле категории в самой форме проверки:

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()
...