Show objects properly as choices in django forms choicefield - django

I am working on some kind of testing application. I have two models for tests and users answers:
class TestModel(AbstractDatetimeModel):
number = models.IntegerField(unique=True)
answer_1 = models.CharField(max_length=255)
answer_2 = models.CharField(max_length=255)
class AnswerModel(AbstractDatetimeModel):
ANSWERS = (
('1', 'A'),
('2', 'B')
)
candidate = models.ForeignKey(User)
question = models.ForeignKey(TestModel)
answer = models.CharField(max_length=1, choices=ANSWERS)
And I have a form, where candidate (user) can answer each question. To implement this I use a view formset_factory:
def communicative_test(request):
questions = TestModel.objects.all()
question_formset = formset_factory(form=TestForm, extra=questions.count())
question_formset = question_formset(initial=[{'candidate': request.user, 'question': x.number,
'answer_1': x.answer_1, 'answer_2': x.answer_2} for x in questions])
return render(request, 'testing/communicative.html', locals())
On a form I need to show ChoiceField, where choices should be data from fields answer_1 and answer_2. Here is this form:
class TestForm(forms.ModelForm):
answer_1 = forms.CharField()
answer_2 = forms.CharField()
VARIANTS = (
('answer_1', answer_1),
('answer_2', answer_2)
)
variants = forms.ChoiceField(choices=VARIANTS, widget=forms.RadioSelect())
class Meta:
model = TestAnswer
fields = '__all__'
Problem is that on a page these answers displays as radio buttons with following labels:
<django.forms.fields.CharField object at 0x7f36248ef5d0>
<django.forms.fields.CharField object at 0x7f36248ef650>
I need to display it properly.

Well, as #Anentropic mentioned, I was not on the right way.
And he is right, because I need to submit only answer A or B. But for user on the form I need to display text for these answers from Test model. So I wrote two templatetags for this.
class TestForm(forms.ModelForm):
class Meta:
model = TestAnswer
fields = '__all__'
def communicative_test(request):
questions = TestModel.objects.all()
QuestionFormSet = formset_factory(form=TestForm, max_num=questions.count())
formset = QuestionFormSet(initial=[{'candidate': request.user, 'question': x.number} for x in questions])
return render(request, 'testing/communicative.html', locals())
Two custom templatetags for each answer (A/B):
#register.filter(name='get_answer1')
def get_answer1(question):
try:
question = TestModel.objects.get(number=question)
answer = question.answer_1
except ObjectDoesNotExist:
answer = ''
return answer
#register.filter(name='get_answer2')
def get_answer2(question):
try:
question = TestModel.objects.get(number=question)
answer = question.answer_2
except ObjectDoesNotExist:
answer = ''
return answer
And template is:
{% load test_tags %}
<form id="communicative-test-form" method="post">
{% csrf_token %}
{{ question_formset.management_form }}
{% for form in question_formset.forms %}
{% crispy form %}
{{ form.id }}
<p>{{ form.question.value }}</p>
<p><label>
<input type=radio name="form-{{ form.question.value|add:"-1" }}-answer" id='id_form-{{ form.question.value|add:"-1" }}-answer' value="1">
{{ form.question.value|get_answer1 }}
</label></p>
<p><label>
<input type=radio name="form-{{ form.question.value|add:"-1" }}-answer" id='id_form-{{ form.question.value|add:"-1" }}-answer' value="2">
{{ form.question.value|get_answer2 }}
</label></p>
{% endfor %}
<div style="text-align: right;" class="col-md-12">
<button type="submit">Save</button>
</div>
</form>

Related

'modelBlog' object has no attribute 'CommentModel_set'

Im try make blog detail and comment in one page but if im try posting comment i got error like this 'modelBlog' object has no attribute 'CommentModel_set'
My views
def comment(request, id):
blog = get_object_or_404(modelBlog, id=id)
form = CommentForm(request.POST or None)
heading = blog.title
if request.method == 'POST':
if form.is_valid():
data={
'name':request.POST['name'],
'komentar':request.POST['komentar'],
}
blog.CommentModel_set.create(**data)
return redirect('blog:detail', id)
context = {
'title':'Detail',
'heading': heading,
'blog':blog,
'comment':comment,
'form':form
}
return render(request, 'blog/detail.html', context)
And my model
class modelBlog(models.Model):
title = models.CharField(max_length=200)
description = models.TextField()
body = models.TextField()
pub_date = models.DateTimeField(auto_now_add=True,)
def __str__(self):
return ('{}.{}').format(self.id, self.title)
class CommentModel(models.Model):
user = models.ForeignKey('auth.User',default=1, on_delete=models.CASCADE)
blog = models.ForeignKey(modelBlog, on_delete=models.CASCADE)
name = models.CharField(max_length=200)
komentar = models.TextField()
pub_date = models.DateTimeField(auto_now_add=True,)
My forms
class CommentForm(forms.ModelForm):
class Meta:
model = CommentModel
fields = [
'name',
'komentar',
]
widgets = {
'name': forms.TextInput(attrs={'class':'form-control'}),
'komentar': forms.Textarea(attrs={'class':'form-control'}),
}
}
My templates
<form action="{% url 'blog:comment' blog.id %}" method="post">
{% comment %} {% endcomment %}
{% csrf_token %}
{% for forms in form %}
<div class="mb-3">
<label for="exampleFormControlInput1" class="form-label">{{forms.label}}</label>
<input type="{{forms.type}}" name="{{forms.name}}" class="form-control" id="exampleFormControlInput1">
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">Kirim</button>
</form>
Don't use uppercase! You must omit capital letters when use "_set"
blog.commentmodel_set.create(**data)
From the doc:
"If you don’t specify a related_name attribute for a field in an abstract base class, the default reverse name will be the name of the child class followed by '_set', just as it normally would be if you’d declared the field directly on the child class. For example, in the above code, if the related_name attribute was omitted, the reverse name for the m2m field would be childa_set in the ChildA case and childb_set for the ChildB field."

fields do not recognise django

I have been having a problem working with formsets in my project and I've been trying to get to the bottom of this. While doing so, a couple of different errors have been appearing. Generally, what I want to do is create an object of entity A (workout) and get redirected to a template/url that lets me "fill" it with objects of entity B, which I will be making at that point dynamically using model formsets. The problem seems to be revolving around the form, more specifically: if I write the fields one by one, as in :
CycleFormSet = modelformset_factory(
Cycle, fields=('reps', 'place_in_workout', 'exercise', 'number_of_times', 'break_inbetween'), extra=1
)
Then, I get the error: Unknown field(s) (place_in_workout, break_inbetween, reps, number_of_times) when I attempt to run the server. If I use exclude for some field, or do fields = 'all' , then I don't get an error at this point. However, I get the error : ['ManagementForm data is missing or has been tampered with'] when I try to post the data of the workout object. Me code:
models.py
class Exercise(models.Model):
name = models.CharField(max_length=150)
description = models.TextField(max_length=500)
def __str__(self):
return self.name
class Workout(models.Model):
name = models.CharField(max_length=150, null=True)
created_by_user = models.ForeignKey(User, null=True, on_delete=models.RESTRICT)
description = models.TextField(max_length=1000, null=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Cycle(models.Model):
place_in_workout = models.IntegerField
exercise = models.ManyToManyField(Exercise)
number_of_times = models.IntegerField
reps = models.IntegerField
break_inbetween = models.IntegerField
workout = models.ManyToManyField(Workout)
class WorkoutCompleted(models.Model):
datetime = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, on_delete=models.RESTRICT)
forms.py
class CreateUserForm(UserCreationForm):
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
class WorkoutForm(forms.ModelForm):
class Meta:
model = Workout
fields = ['name', 'description']
class ExerciseForm(forms.ModelForm):
class Meta:
model = Exercise
fields = ['name', 'description']
CycleFormSet = modelformset_factory(
Cycle, fields='__all__', extra=1
)
urls.py
urlpatterns = [
path('register/', views.register_page, name='register'),
path('login/', views.login_page, name='login'),
path('logout', views.logout_page, name='logout'),
path('', views.index, name='index'),
path('browse/', views.browse, name='browse'),
path('workouts/<str:user_id>/', views.workouts, name='workouts'),
path('add_exercise/', views.add_exercise, name='add_exercise'),
path('create_workout/<str:user_id>/', views.fill_workout, name='fill_workout')
]
views.py
#login_required(login_url='login')
def workouts(request, user_id):
context = {}
if request.method == 'POST':
form = WorkoutForm(request.POST)
if form.is_valid():
workout = form.save(commit=False)
workout.created_by_user = request.user
workout.save()
workout_id = workout.id
context = {'workout_id': workout_id}
return render(request, 'Trainmate/fill_workout.html', context)
else:
form = WorkoutForm()
workout_programs = Workout.objects.all()
user_workouts = workout_programs.filter(created_by_user=user_id)
context = {'user_workouts': user_workouts, 'form': form}
return render(request, 'Trainmate/workouts.html', context)
#login_required(login_url='login')
def fill_workout(request, user_id):
if request.method == 'POST':
# workouts = Workout.objects.filter(created_by_user__exact=request.user).order_by('-created_at')
# current_workout = workouts[0]
# pk_workout = current_workout.id
pk_workout = 1
formset = CycleFormSet(request.POST)
if formset.is_valid():
for form in formset:
cycle = form.save(commit=False)
cycle.workout = Workout.objects.get(pk=pk_workout)
cycle.save()
context = {}
return render(request, 'Trainmate/home.html', context)
else:
formset = CycleFormSet(queryset=Cycle.objects.none())
context = {'formset': formset}
return render(request, 'Trainmate/fill_workout_with_sets', context)
(there are more views, I didn't include some views about login/logout, if asked, I will, I didn't want to make the post even bigger than it's already going to be). Also, I have run the views with the commented section, I believe I am doing some mistake with queryset, therefore I gave the pk_workout=1 so that the fault in the query set is not relevant. There is at least a workout object in the database at all times.
workouts.html
{% extends 'Trainmate/main.html' %}
{% block content %}
<h1>My Workouts</h1>
<div>
{% for workout in user_workouts %}
<tr>
<td>{{ workout.name }}</td>
<td><a class="btn btn-sm btn-info" href="">Update</a></td>
<td><a class="btn btn-sm btn-info" href="">Delete</a></td><br>
</tr>
{% endfor %}
</div>
<h1>Create new Workout</h1>
<form method="POST" action="{% url 'fill_workout' request.user.id %}">
{% csrf_token %}
{{ form }}
<input type="submit" value="Create Workout">
</form>
{% endblock %}
fill_workout.html
{% extends 'Trainmate/main.html' %}
{% block content %}
<h1>Fill workout with sets</h1>
<form id="form_container" method="POST" action="">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
<div class="set_form">
{{ form.as_p }}
</div>
{% endfor %}
<button id="add-form" type="button">Add another set</button>
<button type="submit"> Create Cycle</button>
</form>
let set_form = document.querySelectorAll(".set_form")
let container = document.querySelector("#form_container")
let add_button = document.querySelector("#add_form")
let total_forms = document.querySelector("#id_form-TOTAL-FORMS")
let form_num = set_form.length -1
add_button.addEventListener('click',add_form)
function add_form(e){
e.preventDefault()
let new_form = set_form[0].cloneNode(true)
let form_regex = RegExp(`form-(\\d){1}-`,'g')
form_num ++
new_form.innerHTML = new_form.innerHTML.replace(form_regex, `form-${form_num}-`)
container.insertBefore(new_form, add_button)
total_forms.setAttribute('value', `${form_num + 1}`)
}
{% endblock %}
I tried to run the server and complete the form with the name and description of the workout object without the javascript part of the template above, I still get the same error.
Sorry for the long post and the , I have been trying to narrow down my problem as much as I can before posting, but it seems I get nowhere.
You need to initialise the fields when you define them in your models, you are missing the parenthesis () from your model fields in the Cycle model
class Cycle(models.Model):
place_in_workout = models.IntegerField()
exercise = models.ManyToManyField(Exercise)
number_of_times = models.IntegerField()
reps = models.IntegerField()
break_inbetween = models.IntegerField()
workout = models.ManyToManyField(Workout)

django class based view pass url parameter in create post to current category

lets's say that i have three categories (tutorials, news, jobs).
and i have class based views to list all posts, list posts by category and create new posts.
and sure post is the same model and fields to all categories.
my problem is :
if user was in category list template (let's say tutorial) .. i want the user when he create new post .. it is saved directly to tutorial category .. and if user was in list template (let's say news) .. he will create new post which will be saved directly to news category.
i mean create new post saved directly to current category.
i believe i will use (pass url parameter to class based views) but actually i failed to do that .. and i searched tonnage of questions without got what i want.
can any body help .. with sample please.
models.py
class Category(models.Model):
name = models.CharField(max_length=50)
slug = models.SlugField(max_length=50, unique=True)
def save(self, *args, **kwargs):
if not self.slug and self.name:
self.slug = slugify(self.name)
super(Category, self).save(*args, **kwargs)
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
views.py
def PostListView(request, category_slug=None):
category = None
posts = Post.objects.all().prefetch_related().annotate(commentscountperpost=Count('comments'))
categories = Category.objects.prefetch_related().annotate(total_product_category=Count('post'))
if category_slug:
category = Category.objects.get(slug=category_slug)
posts = posts.filter(category=category)
context = {
'title': 'Home Page',
'posts': posts,
'total_posts': total_posts,
'categories': categories,
'category': category,}
return render(request, 'blog/index.html', context)
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
template_name = 'blog/new_post.html'
form_class = PostCreateForm
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
forms.py
class PostCreateForm(forms.ModelForm):
title = forms.CharField(label='Title')
content = forms.CharField(label='Content', widget=forms.Textarea)
class Meta:
model = Post
fields = ['title', 'content']
urls.py
path('index_list/', PostListView, name='list'),
path('<slug:category_slug>', PostListView, name='post_category_list'),
path('new_post/', PostCreateView.as_view(), name='new_post'),
new_post.html template
{% extends 'base.html' %}
{% block content %}
{% load crispy_forms_tags %}
<div class="border p-4 mb-5">
<legend class="border-bottom pb-1 mb-3">New Post </legend>
<form method="POST">
{% csrf_token %}
{{form|crispy}}
<input class="btn btn-secondary mt-4" type="submit" value="Add New Post">
</form>
</div>
{% endblock content %}
list.html template
{% for category in categories %}
<h5><a class="text-primary" href="{% url 'post_category_list' category.slug %}">
{{ category.name }} ({{ category.total_product_category }})</a></h5>
{% endfor %}
<h5>{{ total_posts }} Total Posts </h5>
{% if category %}
New {{ category }}
{% endif %}
You can try like this:
class PostCreateView(generic.CreateView):
model = Post
template_name = 'blog/new_post.html'
form_class = CreatePostForm
slug_url_kwarg = 'slug'
def form_valid(self, form):
category = Category.objects.get(slug=self.kwargs['slug'])
form.instance.category = category
form.instance.author = self.request.user
return super(PostCreateView, self).form_valid(form)
And in the urls
path('new_post/<slug>/', PostCreateView.as_view(), name='new_post'),
yes i found it , depending on arjun answer, greate thanks for arjun
in list posts per category template change :
New {{ category }}
to:
New {{ category }}
and it works fine,
thanks.

Django foreign key drop down

New to Django and Python and I need a little help with a foreign key drop down. Basically, I have a category model and a image model and I want users to be able to choose which category to put the image in. How do I create a drop down for the category in the image form? Are my views and html correct too? I have had a look online but I can't seem to do it myself. I keep getting errors.
Here are my models:
class Images(models.Model):
image = models.ImageField(upload_to='images', blank=False)
img_name = models.CharField(max_length=120, blank=True)
img_date = models.DateTimeField(default=now())
img_user = models.ForeignKey(User)
img_cat_id = models.ForeignKey(Categories)
def __unicode__(self):
return self.img_name
class Categories(models.Model):
cat_descr = models.CharField(max_length =120, blank=False)
def __unicode__(self):
return self.cat_descr
VIEWS:
#login_required
def upload_images(request):
context = RequestContext(request)
context_dict={}
if request.method == 'POST': # render the form, and throw it back.
# take the form data and process it!
form = UploadImagesForm(request.POST, request.FILES)
if form.is_valid():
print 'form is_valid'
upload_image = form.save(commit=False)
upload_image.img_user = request.user
if 'image' in request.FILES:
upload_image.image =request.FILES['image']
upload_image.save()
return render(request, 'rmb/upload.html', {'upload_image': form})
else:
print form.errors
# Not a HTTP POST, so we render our form using two ModelForm instances.
# These forms will be blank, ready for user input.
else:
form = UploadImagesForm()
context_dict = {'upload_image': form}
all_categories = Categories.objects.order_by('-id')
context_dict['all_categories'] = all_categories
print context_dict
return render_to_response('rmb/upload.html', context_dict, context)
FORMS:
class UploadImagesForm(forms.ModelForm):
#cat_list = ModelChoiceField(queryset=Categories.objects.all())
class Meta:
model = Images
fields=('image','img_name')
HTML:
{% block body_block %}
<form id="upload_form" method="post" action="/rmb/upload/"
enctype="multipart/form-data">
{% csrf_token %}
{{ upload_image.as_table }}
<input type="submit" name="submit" value="Upload" />
{% for categories in all_categories %}
<div> {{ categories.id }} </div>
{{ categories.cat_descr }}
<input type="submit" name="submit" value="Upload" />
{% endfor %}
</form>
{% endblock %}
You don't need to insert the HTML for the form manually, just use {{form}} in the template.
{% block body_block %}
<form id="upload_form" method="post" action="/rmb/upload/"
enctype="multipart/form-data">
{% csrf_token %}
{{ form }}
</form>
{% endblock %}
By default a ForeignKey will be a select field so you shouldn't need to do much else.
As an aside, give your models and fields more appropriate names. We know these are all image fields, because they are on the image and make sure, unless your model is a collection of things, you give it a singular name. Lastly, when using a Foreign Key and item gets an extra field of fieldname_id that is just the ID, whereas fieldname is the property that gives the related item as well.
So instead of:
class Images(models.Model):
image = models.ImageField(upload_to='images', blank=False)
img_name = models.CharField(max_length=120, blank=True)
img_date = models.DateTimeField(default=now())
img_user = models.ForeignKey(User)
img_cat_id = models.ForeignKey(Categories)
Use:
class Image(models.Model):
image = models.ImageField(upload_to='images', blank=False)
name = models.CharField(max_length=120, blank=True)
date = models.DateTimeField(default=now())
user = models.ForeignKey(User)
category = models.ForeignKey(Categories)

One form for two models

UPDATE The issue is solved, all the code you can see works.
Hello!
I have a ForeignKey relationship between TextPage and Paragraph and my goal is to make front-end TextPage creating/editing form as if it was in ModelAdmin with 'inlines': several fields for the TextPage and then a couple of Paragraph instances stacked inline. The problem is that i have no idea about how to validate and save that:
#login_required
def textpage_add(request):
profile = request.user.profile_set.all()[0]
if not (profile.is_admin() or profile.is_editor()):
raise Http404
PageFormSet = inlineformset_factory(TextPage, Paragraph, fields=('title', 'text', ), extra=5)
textpage = TextPage()
if request.POST:
textpageform = TextPageForm(request.POST, instance=textpage, prefix='page')
formset = PageFormSet(request.POST, instance=textpage, prefix='paragraphs')
# Saving data
if textpageform.is_valid():
textpageform.save()
if formset.is_valid():
formset.save()
return HttpResponseRedirect(reverse(consult_categories))
else:
textpageform = TextPageForm(instance=textpage, prefix='page')
formset = PageFormSet(instance=textpage, prefix='paragraphs')
return render_to_response('textpages/manage.html', { 'formset' : formset,
'textpageform' : textpageform,
}, context_instance=RequestContext(request))
I know it's a kind of code-monkey style to post code that you don't even expect to work but I wanted to show what I'm trying to accomplish. Here is the relevant part of models.py:
class TextPage(models.Model):
title = models.CharField(max_length=100)
page_sub_category = models.ForeignKey(PageSubCategory, blank=True, null=True)
def __unicode__(self):
return self.title
class Paragraph(models.Model):
article = models.ForeignKey(TextPage)
title = models.CharField(max_length=100, blank=True, null=True)
text = models.TextField(blank=True, null=True)
def __unicode__(self):
return self.title
Any help would be appreciated. Thanks!
UPDATE. Instance references added, but still doesn't work - results in a ValidationError on this string:
formset = PageFormSet(request.POST, instance=textpage, prefix='paragraphs')
Any ideas?
The updated code with instance references actually works fine! The problem was in the template: I forgot the ManagmentForm. Here is the template code:
{% extends "site_base.html" %}
{% block body %}
<form action="" method="post">
{{ textpageform.as_p }}
{{ formset.management_form }}
{% for form in formset.forms %}
<p>{{ form.as_p }}</p>
{% endfor %}
<input type="submit" value="Go" />
{% endblock %}
Hope this example helps newbies like me :)