问题
I have a problem when I want to save the objects such as the Tags
, and always returned an error because form validation.
Select a valid choice.
hello
is not one of the available choices.
Here, I want to implement the select input dynamically which customs additional value from the users creation.
For the frontend demo, like this snippet: https://jsfiddle.net/agaust/p377zxu4/
As conceptually, the
tags input
provide available tags that already created before... But, the important thing is the Users are allowed to create additional tags what they wants.
1. here is my models.py
@python_2_unicode_compatible
class Tag(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, unique=True)
def __str__(self):
return self.title
class Meta:
verbose_name = _('Detail Tag')
verbose_name_plural = _('Tags')
@python_2_unicode_compatible
class Thread(TimeStampedModel):
title = models.CharField(max_length=200)
....
tags = models.ManyToManyField(
Tag, blank=True, related_name='tags_thread')
2. forms.py
from myapp.models import (Tag, Thread)
class ThreadForm(forms.ModelForm):
description = DraceditorFormField()
tags = forms.ModelMultipleChoiceField(
to_field_name='slug', # set the value to slug field, not pk/id
required=False,
label=_('Additional tags'),
help_text=_('Sparate by comma to add more than once, or select from available tags'),
queryset=Tag.objects.all(),
widget=forms.SelectMultiple(attrs={
'placeholder': _('Additional tags'),
'class': 'ui search fluid dropdown dropdown-add-tags'
})
)
class Meta:
model = Thread
fields = '__all__'
exclude = [
'author', 'topic', 'rating',
'created', 'modified'
]
widgets = {
'title': forms.TextInput(attrs={'placeholder': _('Title')})
}
def clean(self):
# this condition only if the POST data is cleaned, right?
cleaned_data = super(ThreadForm, self).clean()
print(cleaned_data.get('tags')) # return queryset of tags
3. views.py
def save_tagging(post_getlist_tags):
"""
return value list of slugs from the filed of `tags`.
allow to create if the tag is doesn't exist.
this function bassed on slug field.
:param `post_getlist_tags` is request.POST.getlist('tags', [])
"""
cleaned_slug_tags = []
for value in post_getlist_tags:
slug = slugify(value)
if Tag.objects.filter(slug=slug).exists():
cleaned_slug_tags.append(slug)
else:
tag = Tag.objects.create(title=value, slug=slug)
cleaned_slug_tags.append(tag.slug)
return cleaned_slug_tags
@login_required
def thread_new(request, topic_slug):
....
topic = get_object_or_404(Topic, slug=topic_slug)
if request.method == 'POST':
form = ThreadForm(request.POST, instance=Thread())
if form.is_valid():
initial = form.save(commit=False)
initial.author = request.user
initial.topic = topic
# set tagging, this will not being executed because error form validation
initial.tags = save_tagging(request.POST.getlist('tags', []))
initial.save()
form.save()
else:
# forms.errors # goes here..
Let checkout what I have when I typing the additional tags,
<select multiple="multiple" id="id_tags" name="tags" placeholder="Additional tags">
<option value="hello" class="addition">hello</option>
<option value="albacore-tuna" class="addition">albacore-tuna</option>
<option value="amur-leopard" class="addition">amur-leopard</option>
<option value="This other once" class="addition">This other once</option>
</select>
This why I implement my field of
tags
in the form is like this...
tags = forms.ModelMultipleChoiceField(
to_field_name='slug'
....
)
I would be very appreciated for the answers... :)
Update Solved
Thank you so much for @Resley Rodrigues for help.. Finally, I got it without the form field... only handled in the views and the template.
def save_tagging(post_getlist_tags):
"""
return objects list of tags.
allow to create if the tag is doesn't exist.
this function bassed on slug field.
:param `post_getlist_tags` is request.POST.getlist('fake_tags', [])
"""
cleaned_tags = []
for value in post_getlist_tags:
slug = slugify(value)
if Tag.objects.filter(slug=slug).exists():
tag = Tag.objects.filter(slug=slug).first()
cleaned_tags.append(tag)
else:
# makesure the slug is not empty string.
# because I found the empty string is saved.
if bool(slug.strip()):
tag = Tag.objects.create(title=value, slug=slug)
tag.save()
cleaned_tags.append(tag)
return cleaned_tags
@login_required
def thread_new(request, topic_slug):
....
if request.method == 'POST':
form = ThreadForm(request.POST, instance=Thread())
if form.is_valid():
initial = form.save(commit=False)
initial.author = request.user
....
form.save()
# set tagging after created the object
saved_tags = save_tagging(request.POST.getlist('fake_tags', []))
initial.tags.add(*saved_tags)
and the templates.html
using field named by fake_tags
, I just think it should hasn't crash with field that already named by tags
.
<select name="fake_tags" multiple="multiple" class="ui search fluid dropdown dropdown-add-tags"></select>
<script>
$(document).ready(function() {
$('.ui.dropdown.dropdown-add-tags').dropdown({
placeholder: '{% trans "Additional tags" %}',
allowAdditions: true,
minCharacters: 3,
apiSettings: {
url: 'http://api.semantic-ui.com/tags/{query}'
}
});
});
</script>
For edit mode, add the following these lines below after end-tag of $('.ui.dropdown.dropdown-add-tags').dropdown({...});
thread
is instance object.
var items = [{% for tag in thread.tags.all %}"{{ tag.title }}"{% if not forloop.last %},{% endif %}{% endfor %}];
$('.ui.dropdown.dropdown-add-tags').dropdown(
'set selected', items
);
来源:https://stackoverflow.com/questions/41809057/django-form-select-multipe-allow-to-add-the-objects