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
);
Related
So i want to know how to pin posts in django. When i click on check, and click create post, it should pin the post to the top. And newer posts will just appear bottom, without interfering with the pinned post. The pin system should also be able to pin multiple posts. So if i pin another post, both posts should stay at the top. The other not pinned posts should just appear below.
models.py
class AskSection(models.Model):
title = models.CharField(max_length=100)
description = models.TextField()
user = models.ForeignKey(User, on_delete=models.CASCADE)
is_pin = models.BooleanField()
likes = models.ManyToManyField(User, related_name='likes', blank=True)
is_marked = models.BooleanField(default=False)
date_posted = models.DateTimeField(default=timezone.now)
class Meta:
ordering = ['-date_posted']
verbose_name_plural = "Ask Section"
def __str__(self):
return str(self.title)
forms.py
class AskSectionCreateForm(forms.ModelForm):
is_pin = forms.BooleanField(label="pin ask post", required=False)
class Meta:
model = AskSection
fields = ('title', 'description', 'is_pin')
widgets = {
'title': forms.TextInput(attrs={
'class': 'form-control'
}),
'description': forms.Textarea(attrs={
'class': 'form-control'
}),
}
views.py
#login_required
def create_ask_post(request):
if request.method == "POST":
form = AskSectionCreateForm(request.POST or None)
if form.is_valid():
title = form.cleaned_data.get('title')
description = form.cleaned_data.get('description')
is_pin = form.cleaned_data.get('is_pin')
obj = form.save(commit=False)
obj.title = title
obj.description = description
obj.is_pin = is_pin
obj.user = request.user
obj.save()
messages.success(request, f'You have posted {title} successfully')
return redirect('/details_ask/' + str(title) + '/')
else:
form = AskSectionCreateForm()
else:
form = AskSectionCreateForm()
context = {
'form': form
}
return render(request, "editor/create_ask_post.html", context)
html file
{% for question in all_questions %}
<!-- random HTML not important code --> <!-- code is related to just the styling of posts and just model fields -->
{% endfor %}
so please let me know how to do this. The HTML file isn't really important. It just contains the card, and model fields.
Thanks!
So you don't really need another field in your model you could just work with your DateTimeField but as commented before I would add a rank = models.Integerfield(default=0) to the AskSection model. (Don't forget to migrate)
you have a views.py file with a function where you set the context for your html (not the one you showed in your answer but the other one where you define all_questions). here you can set an order for all_questions like so all_questions = AskSection.objects.filter(user=request.user).order_by("rank", "-is_pin"). Your code might look a little different now I dont know if you filter by user I just assumed that...
When a User adds a new question you increase your the rank on the new question so you always have a clean order. Whenever a user "Pins" a question you take highest rank and add a number + set the Boolean to True.
An alternative way would be working with the Datefield date_posted like so
all_questions = AskSection.objects.filter(user=request.user).order_by("date_posted", "-is_pin"). in that case the date would act as a "rank". saves you a migration but its not as flexible as a Integerfield.
I am trying to auto populate my form Student field with the name Carl Park. In short I want my form to use to automatically use the Object ID instead of having to manually select the name Carl Park from the student field drop down menu.
How do I automatically link my Model Form to a foriegn key ID and have the form auto populate based on that value.
Here is my current form, my models.py forms.py views.py and HTML
#models
class Teacher(models.Model):
name = models.CharField(max_length=75)
room = models.CharField(max_length=10)
bio = models.TextField()
class Student(models.Model):
teacher = models.ForeignKey('Teacher', on_delete=models.CASCADE)
name = models.CharField(max_length=75)
class Student_Performance(models.Model):
student = models.ForeignKey('Student', on_delete=models.CASCADE)
behavior_grade = models.CharField(max_length=1)
#form
class StudentPerfromanceForm(ModelForm):
class Meta:
model = Student_Performance
fields = ( "name", "behavior_grade")
#view
def student_details(request, id):
obj = get_object_or_404(Student,pk=id)
form = StudentPerfromanceForm()
if request.method == 'POST':
form = StudentPerfromanceForm(request.POST)
if form.is_valid():
form.save(commit=True)
return all_students_list(request) #pass success message
return render(request, 'class_roster/student_details.html', {'obj':obj, 'reviewForm':form})
#HTML
<form method="POST">
{% csrf_token %}
{{reviewForm.as_p}}
<input type="submit" value="Submit Review">
</form>
I want to more code informations.
anyway if you want set student automatically use object_id,
try
objects.get(query)
ex) ModelClassName.objects.get(pk=1)
I am fairly new to Django and struggling a bit on how to get the primary keys from form input on a multiform view. I cannot get the keys into the database.
I have 3 models: Human, Human_notes, and Location. My form is made of these separate models as forms on one view. I thought I should collect the primary key from each form once saved and apply that to the next form data as a foreign key...
<form action="human-add" method="POST">
{% csrf_token %}
{{ human_form.as_p }}
{{ location_form.as_p }}
{{ human_notes_form.as_p }}
<button type="submit" class="save btn btn-success">Save</button>
</form>
Human has FKs to Location...:
class Human(models.Model):
intaker = models.ForeignKey(User, default=None, on_delete=models.SET_NULL, null=True)
location = models.OneToOneField('Location', on_delete=models.SET_NULL, null=True, related_name='humans')
Human_notes has FK to Human...(maybe this will become an FK in Human but originally thought many notes for one human) :
class HumanNotes(models.Model):
human = models.ForeignKey(Human, on_delete=models.SET_NULL, null=True, related_name='humans_notes')
My view is:
def human_add(request):
if request.method == 'POST':
human_form = HumanForm(request.POST)
location_form = LocationForm(request.POST)
human_notes_form = HumanNotesForm(request.POST)
if human_form.is_valid() and location_form.is_valid() and human_notes_form.is_valid():
human_form.save(commit=False)
location_form.save()
locationKey = location_form.id
human_notes_form.save(commit=False)
human_notes_form.intaker = request.user
human_notes_form.save()
noteKey = human_notes_form.id
human_form.location = locationKey
human_form.note = noteKey
human_form.intaker = request.user
human_form.save()
return redirect('/intake/success-form')
else:
context = {
'human_form': human_form,
'location_form': location_form,
'human_notes_form': human_notes_form,
}
else:
context = {
'human_form': HumanForm(),
'location_form': LocationForm(),
'human_notes_form': HumanNotesForm(),
}
return render(request, 'intake/human-add.html', context)
The only error I am getting is that 'LocationForm' object has no attribute 'id' - but I even added it explicitly (thought I should not have to and don't want it visible):
class HumanNotesForm(forms.ModelForm):
class Meta:
model = HumanNotes
fields = ['id','intaker','note']
class LocationForm(forms.ModelForm):
class Meta:
model = Location
fields = ['id','st1','st2','cty','county','state','zip','lat','long','img','img_nm','img_id']
Any guidance appreciated.
This did the trick....as did getting excellent guidance from forum.djangoproject.com. Needed to understand the diff between forms and models.
def human_add(request):
if request.method == 'POST':
human_form = HumanForm(request.POST)
location_form = LocationForm(request.POST)
human_notes_form = HumanNotesForm(request.POST)
if human_form.is_valid() and location_form.is_valid() and human_notes_form.is_valid():
loc = location_form.save()
hum = human_form.save(commit=False)
humnote = human_notes_form.save(commit=False)
hum.intaker = request.user
hum.location = loc #.id NOTE YOU NEED THE ENTIRE INSTANCE
human_form.save()
humnote.intaker = request.user
humnote.human = hum #.id NOTE YOU NEED THE ENTIRE INSTANCE
human_notes_form.save()
return redirect('/intake/success-form')
else:
context = {
'human_form': human_form,
'location_form': location_form,
'human_notes_form': human_notes_form,
}
else:
context = {
'human_form': HumanForm(),
'location_form': LocationForm(),
'human_notes_form': HumanNotesForm(),
}
return render(request, 'intake/human-add.html', context)
I'm trying to give django a try by developing a simple page where people can ask something about a product
This is my model, i can create products in the admin area, display the product page, and the form shows up with the fields email, and text.
class Product(models.Model):
category = models.ForeignKey(Category)
title = models.CharField(max_length=100)
text = models.TextField()
class Question(models.Model):
email = models.CharField(max_length=100)
product = models.ForeignKey(Product, default=?, editable=False)
date = models.DateTimeField(auto_now=True, editable=False)
text = models.TextField()
class QuestionForm(ModelForm):
class Meta:
model = Question
But i don't know how to tell the model which product id the question has to be saved to.
This is my views.py
# other stuff here
def detail(request, product_id):
p = get_object_or_404(Product, pk=product_id)
f = QuestionForm()
return render_to_response('products/detail.html', {'title' : p.title, 'productt': p, 'form' : f},
context_instance = RequestContext(request))
def question(request, product_id):
p = get_object_or_404(Product, pk=product_id)
f = QuestionForm(request.POST)
new_question = f.save()
return HttpResponseRedirect(reverse('products.views.detail', args=(p.id,)))
And the URL
urlpatterns = patterns('products.views',
(r'^products/$', 'index'),
(r'^products/(?P<product_id>\d+)/$', 'detail'),
(r'^products/(?P<product_id>\d+)/question/$', 'question')
)
Righ now it works if i put a "1" in the default attribute for the product foreign key in the question model (where the question mark is), it saves the question to the product id 1. But i don't know what to do to make it save to the current product.
You can either:
Send product_id as form value
Make product foreign key in your form a hidden field and set it's value to primary key value of the product in detail view. This way you don't need a product_id in your question view URL and arguments since the ID will be passed with POST data. (see link for examples)
Id would use this option, since you'd have cleaner question URL and you could do more validation in your form on the product.
or
Send product_id through URL
Use reverse in your detail view to build the form action attribute or use url template tag to build the action attribute in form template. This way you need product_id in your question URL and arguments but you don't need product field in your QuestionForm. Then in question view simply get the product instance and set it as FK value on Question.
Example:
Using url template tag in products/detail.html:
<form action="{% url question product.pk %}" method="post">
....
</form>
Or use reverse in detail view:
def detail(request, product_id):
p = get_object_or_404(Product, pk=product_id)
f = QuestionForm()
return render_to_response('products/detail.html', {
'title' : p.title,
'product': p,
'action': reverse("question", args=[p.pk,]),
'form' : f},
context_instance = RequestContext(request))
your template:
<form action="{{ action }}" method="post">
....
</form>
Either way your question view would need a line added which actually sets product instance to Question attribute:
def question(request, product_id):
...
new_question.product = p
python=2.7, django=1.11.13
In my html I am not able to display my condition choices from my models.py
When filling the form, the user is not able to choose a condition because they are not displayed.
models.py
class Books(models.Model):
book_name = models.CharField(max_length=100)
book_condition = models.ForeignKey('Condition')
def __unicode__(self):
return self.book_name
class Condition(models.Model):
NEW = 'new'
USED = 'used'
COLLECTIBLE = 'collectible'
CONDITION_CHOICES = (
(NEW, 'New'),
(USED, 'Used'),
(COLLECTIBLE, 'collectible'),
)
book = models.ForeignKey(Books)
condition = models.CharField(max_length=10, choices=CONDITION_CHOICES)
def __unicode__(self):
return self.condition
views.py
def add_book(request):
if request.method == 'GET':
context = {
'form': BookForm()
}
if request.method == 'POST':
form = BookForm(request.POST)
if form.is_valid():
form.save()
context = {
'form': form,
}
return render(request, 'add_book_form.html', context=context)
add_book_form.html
{% extends 'base.html' %}
{% block body %}
<h3>Add Book </h3>
<form action="" method="post">
{% csrf_token %}
{{ form}}
<br/>
<input class="button" type="submit" value="Submit"/>
</form>
{% endblock %}
And this is my form, I'm not sure what I am missing.
form
from django.forms import ModelForm
from .models import Books, Condition
class BookForm(ModelForm):
class Meta:
model = Books
fields = '__all__'
class ConditionForm(ModelForm):
class Meta:
model = Condition
fields = '__all__'
The form you're passing to the view is a BookForm, the BookForm contains a ForeignKey field to the Condition model, so the options in the select will be instances of the Condition model.
You would need to preemptively create the Condition model instances, via the admin interface or the shell, and then you could see the conditions on the select, but that won't help, because your Condition instance needs to be associated to a Book, and that makes me think your software is badly designed.
Let me propose a solution:
class Book(models.Model):
"""
This model stores the book name and the condition in the same
table, no need to create a new table for this data.
"""
NEW = 0
USED = 1
COLLECTIBLE = 2
CONDITION_CHOICES = (
(NEW, 'New'),
(USED, 'Used'),
(COLLECTIBLE, 'Collectible'),
)
name = models.CharField(max_length=100)
condition = models.SmallIntegerField(choices=CONDITION_CHOICES)
def __unicode__(self):
return "{0} ({1})".format(self.book_name, self.condition)
class BookForm(ModelForm):
class Meta:
model = Book
fields = '__all__'
Now the conditions are saved as an integer (like it would be if you used foreign keys) and your software is easier to understand and develop.
Try to use Django widgets. For example:
class BookForm(forms.Form):
categories = (('Adventure', 'Action'),
('Terror', 'Thriller'),
('Business', 'War'),)
description = forms.CharField(max_length=9)
category = forms.ChoiceField(required=False,
widget=forms.Select,
choices=categories)