Django Autocomplete Light создаст новый выбор
Я работал над следующим учебным пособием для Django Autocomplete Light:
https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html
Я успешно реализовал автозаполнение для одного из полей в моей форме, однако я не могу заполнить следующий раздел:
https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html
В документации говорится, что я должен иметь возможность добавить функцию, которая позволяет пользователю создавать новый выбор в форме, если его требуемый выбор недоступен. Однако в руководстве не очень ясно объясняется, как это сделать.
Я пытаюсь реализовать форму, в которой пользователь может создать новую обратную связь с помощью:
- Выбор из списка автозаполнения категорий
- Выбор сообщения, соответствующего выбранной категории
- Если категория или сообщение, которое они хотят выбрать, недоступны, они должны иметь возможность добавить к существующим вариантам
Я частично это реализовал, но он не работает должным образом, как будто ни одна категория не выбрана, раскрывающийся список для сообщений отображает список категорий. Однако, если выбрана категория, правильные сообщения отображаются по мере необходимости.
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 ответа
После поиска по всей документации с открытым исходным кодом 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 должна быть установлена на первичный ключ соответствующей модели. В моем случае первичным ключом модели категории является имя)
Что не ясно из учебника, так это следующий шаг. После просмотра в следующем файле:
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 возможность создания новой категории в поиске наконец-то сработала!
Теперь у меня возникают трудности при создании объекта 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()
У меня есть модель
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]
Это может быть расширено, чтобы затем обновить несколько строк в модели, если это необходимо, и в вашем случае вы сможете передать ранее выбранную категорию в это определение.
Может быть, проблема в том, что у пользователя нет разрешения на добавление, которое проверяет get_create_option?
Работает ли это, если вы добавите это в свой вид?
def has_add_permission(self, request):
return True