I am building a Poll App and I am stuck on an Problem.
What i am trying to do :-
I am trying to access all three choices of poll from view in template BUT only one choices is showing. BUT when i access Poll object in view and access choice model from template then all three choices are successfully showing.
models.py
class Poll(models.Model):
user = models.ForeignKey(User, null=True, on_delete=models.CASCADE)
title = models.TextField()
def get_absolute_url(self):
return reverse('detail_poll', kwargs={'pk': self.pk})
class Choice(models.Model):
poll = models.ForeignKey(Poll, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=30)
forms.py
class PollAddForm(forms.ModelForm):
choice1 = forms.CharField(label='Choice 1',max_length=100,min_length=2)
choice2 = forms.CharField(label='Choice 2',max_length=100,min_length=2)
choice3 = forms.CharField(label='Choice 3',max_length=100,min_length=2)
class Meta:
model = Poll
fields = ['title','choice1', 'choice2', 'choice3']
I am increasing choices from forms.
views.py
def detail_poll(request,poll_id):
poll = get_object_or_404(Poll, id=poll_id)
for choice in poll.choice_set.all():
printChoice = choice.choice_text
context = {
'printChoice ':printChoice ,
}
return render(request, 'detail_poll.html',context)
In view i am accessing all the choice from choice_text of the poll.
I am accessing three choices for vote with the same (choice_set) method in template.
AND When i create poll then poll is successfully saving with all three choices. When i vote then poll is successfully voting with choices.
BUT when i accessing the choices to calculate percentage from view then choices are not showing.
With the same method of poll.choice_text.all in template, it does work but not from view.
Any help would be much Appreciated.
Thank You in Advance.
It's only showing one choice because you are sending only one choice to context. i.e the last choice. Check your view thoroughly. When the for-loop stops, printChoice will have the last choice and you are sending that to context. So only one choice will be shown in template.
You should iterate over the choices and save them to a data structure like a dict, set, list etc, and then send it to the context.
It should be like this. I have used a list to store the choice_texts and pass it to context.
def detail_poll(request,poll_id):
poll = get_object_or_404(Poll, id=poll_id)
choice_set = []
for choice in poll.choice_set.all():
choice_set.append(choice.choice_text)
# You can use your percentage calculation here...
context = {
'printChoice ': choice_set ,
}
return render(request, 'detail_poll.html',context)
You can also send the entire queryset to context like this.
context = { 'printChoice': poll.choice_set.all() }
And then in template, show the choice_text like this
{% for choice in printChoice %}
<p>choice.choice_text</p>
{% endfor %}
Related
I'm developing to-list app with registration . I have two models : Model for User and Model for Tasks . I add new task throw Ajax to one user it adding and displaying for every user. Is there any solutions ? Here some pictures
Here is my code:
models.py
class Task(models.Model):
title=models.IntegerField()
date = models.DateTimeField(default=datetime.now,blank=True)
is_published=models.BooleanField(default=True)
class CustomUser(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
image=models.FileField(upload_to='photos/%Y/%m/%d/',null=True,blank=True)
views.py
if request.method == 'POST' and request.POST['form_type'] == 'task':
if request.is_ajax():
addtask = AddTask(request.POST)
if addtask.is_valid():
user = request.user.id
addtask.objects.filter(user=user).cleaned_data
addtask.objects.filter(user=user).save()
task_object = Task.objects.filter(user=user)(addtask)
return JsonResponse({'error': False, 'data': task_object})
else:
print(addtask.errors)
return JsonResponse({'error': True, 'data': addtask.errors})
else:
error = {
'message': 'Error, must be an Ajax call.'
}
return JsonResponse(error, content_type="application/json")
addtask = AddTask()
task = Task.objects.order_by('-date').filter(is_published=True)
html page
{% if task %}
{% for tas in task %}
Task content
{% endfor %}
{% else %}
{% endif %}
Maybe you should add relation to CustomUser in Task model and filter tasks by owner in view before to render data to template?
class Task(models.Model):
title=models.IntegerField()
date = models.DateTimeField(default=datetime.now,blank=True)
is_published=models.BooleanField(default=True)
user=models.ForeignKey(CustomUser)
class CustomUser(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
image=models.FileField(upload_to='photos/%Y/%m/%d/',null=True,blank=True)
And in view:
...
addtask = AddTask()
task = Task.objects.filter(is_published=True, user_id=request.user.id).order_by('-date')
So the mistake is that you never connected your CustomUser model with your Task model. They should have a relationship like one to many. Once that is achieved, you have to retrieve only the tasks related to the user of interest from the database and send them to the HTML page. Then only the tasks related to one particular user will be displayed.
If you want to create CustomUser model, you should create a class and inherit it from AbstractBaseUser or AbstractUser(django documentation).
Your Task model hasn't relationships with CustomUser. You create AddTask(?) instance but didn't bind it with any user.
You did not submit a view which renders HTML template, but I think that your query is something like Tasks = Task.objects.all() which return all tasks.
This is how you should create CustomUser model
This is documentation about relationships in Django
This is about making queries in Django
I am donig with a poll system for my class. I use model-form and create-view to serve the poll form. I use choices in in the field but I just find out that create-view only save the last value of the checkboxes and I want to save all the selected choices as a list maybe. I've tried to change the form_valid() method, but I just find out that I need to iterate all the fields to check wheather there are multipule choices. It's not flexible. And I can't figure out other solutions...
How can I meet this requirement? I am truly a newbie..
Thanks in advance.
Thank the friend below for replying in such a short interval after I raised my question. Here is my code.
models.py
CHOICES = (('m','Math'),('f','French'),('s','Science'),('l','literature'))
class Poll(models.Model):
[...]
subject = models.CharField(max_length = 5,choices = CHOICES, blank=True)
[...]`
forms.py
class PollForm(forms.ModelForm):
model = Poll
fields = [..., 'subject', ...]
widgets = {'subject':forms.CheckboxSelectMultiple}
views.py
class PollView(CreateView):
form_class = PollForm
template_name = 'poll.html'
Students can choose subjects they want.
It seems like you need to convert your model. If you could provide a sample of the structure that you are using it would be helpful. Still lets try solving your query. First you need identify that choices is nothing more than a many to many field. Saving it in the db should be a bit easier that way. Lets try taking an example with choices for a user:
class Choices(models.Model):
description = models.CharField(max_length=100)
class UserProfile(models.Model):
user = models.ForeignKey(User, blank=True, unique=True, verbose_name='profile_user')
choices = models.ManyToManyField(Choices)
def __unicode__(self):
return self.name
Now if you want to make a default form you could simply do something like:
class ProfileForm(forms.ModelForm):
Meta:
model = UserProfile
Now comes your main view. This can be editted and rendered to whatever your use case demands it to be:
if request.method=='POST':
form = ProfileForm(request.POST)
if form.is_valid():
profile = form.save(commit=False)
profile.user = request.user
#Implement this as a pre-save so that you can add additional value
profile.save()
else:
form = ProfileForm()
Hope this helps.
I have these models:
# this is model for user
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile')
profilepic = models.ImageField(blank=True)
city = models.ForeignKey(City)
slug = models.SlugField(unique=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.user.username)
super(UserProfile, self).save(*args, **kwargs)
def __unicode__(self):
return self.user.username
#property
def avg_rating(User):
return UserProfile.userrating_set.all().aggregate(Avg('rating'))['rating__avg']
# this is the model for user ratings - one to many relationship with User
class UserRating(models.Model):
user = models.ForeignKey(User)
comment = models.CharField(max_length=500)
for_username = models.CharField(max_length=128)
rating = models.IntegerField(default=5)
def __unicode__(self):
return unicode(self.rating)
I want to refer to the result of the property (avg_rating) in my template so I can show the rating of each user. I tried this it it shows blank result:
{% for user in users %}
{{ user.profile.avg_rating }}
Also, this is the view being invoked:
def index(request):
user_list = User.objects.select_related().order_by('-userrating')[:5]
city_list = City.objects.order_by('-name')[:5]
context_dict = {"users": user_list, "cities" : city_list}
return render(request, "index.html", context_dict)
I'm currently learning my way through Django, sorry if this is very obvious.
The User argument of your avg_rating is not used at all. Try rewriting it as:
def avg_rating(self):
return self.user.userrating_set.all().aggregate(Avg('rating'))['rating__avg']
You can also remove the #property decorator as you can also use methods in Django templates using the same syntax (ie. without ()), such as:
{{ user.profile.avg_rating }}
UserRating is related to User, not UserProfile. You'd need to make the query against that model.
But note that the way you're doing it is extremely expensive, given that it requires an extra query+aggregation for every single user. Instead you should do the query in the view, via the annotation method that works on all elements in a queryset.
def index(request):
user_list = User.objects.select_related().annotate(
rating=Avg('userrating__rating')
).order_by('-rating')[:5]
Now you can refer to each user's rating via {{ user.rating }} in the template.
1)
Things like def avg_rating like to be set as Manager method. It may be better idea to create Manager class for UserProfile (UserProfileManager for example) to deal all queries of UserProfile model. The avg_rating definition would prefer to live in this UserProfileManager.
2)
The avg_rating definition is related to UserProfile so it should be call as UserProfile method not User method.
3)
Setting avg_rating in UserProfile class may be confusing while UserRating class is created for rating stuff.
I have multiple related tables defined in my Django models:
# first models.py
from django.db import models
class Character(models.Model):
first_field = models.DateTimeField()
second_field = models.TextField()
# second models.py
from django.db import models
class Op(models.Model):
fk_character = models.ForeignKey('Character')
some_field = models.DateTimeField()
other_field = models.TextField()
class Participant(models.Model):
fk_op = models.ForeignKey('Op')
fk_character = models.ForeignKey('Character')
some_other_field = models.IntegerField(default=0)
For now, I'm sending this data from a view to template in a way like this:
# views.py
from django.shortcuts import render_to_response
from django.template import RequestContext
from second.models import MainModel
def home(request):
data = Op.objects.filter(some_field__isnull=True).order_by('-date')
rc = RequestContext(request, {'data':data})
return render_to_response('index.html', rc)
In this way I do have all the Op related data I need in my index.html template, but I'm struggling with logic to display this data in my template in a specific way. For example:
display a list of all Ops,
for each list item, check if Character is also a Participant in current Op item,
if it isn't, display some button, if it is than don't display the button
I know that template shouldn't handle any programming logic, but I'm also not sure what would be the best approach to solve this. Should I do all the logic in my view and construct a new object and send that object to my view or is there an easy way to solve this in template with current object I'm sending?
Update your model:
class Op(models.Model):
fk_character = models.ForeignKey('Character')
some_field = models.DateTimeField()
other_field = models.TextField()
def check_if_participant(self):
return bool(self.participant_set.all())
Display list of all Ops:
{% for op in data %}
{{op.some_field}}
{% if op.check_if_participant %}Yes - Character is participant {% endif %}
{% endfor %}
I'm quite new to Django Forms, and I'm facing a problem that I cannot solve. I've been googling and reading the docs, but I can't find the place where this is explained. My problem is that I have an Animal Model and a ModelForm:
class Animal(models.Model):
name = models.CharField(max_length=300)
age = models.PositiveSmallIntegerField()
race = models.ForeignKey(Race)
description = models.TextField()
state = models.ForeignKey(State)
pub_date = models.DateTimeField(auto_now_add=True)
adoption_limit = models.DateTimeField(blank=True, null=True)
location = models.ForeignKey(Location)
publisher = models.ForeignKey(User)
def __unicode__(self):
return self.name
class AnimalForm(ModelForm):
class Meta:
model = Animal
I render this info via urls.py, calling this view:
#login_required
def new_animal(request):
if request.method == "POST":
form = AnimalForm(request.POST)
if form.is_valid():
form.save()
return render_to_response('/')
else:
variables = RequestContext(request, {'e': form.errors})
return render_to_response('web/error.html', variables)
else:
form = AnimalForm()
variables = RequestContext(request, {'form': form})
return render_to_response('web/animal_form.html', variables)
It seems that I have an error introducing the adoption_limit field, so the data does not get saved in DB. This is because I just set a date and not a time into the text field displayed by the form.
I would like to know how can I do two things:
How can I send the error message to the form again, so that I can add a text next to the field that I have not set correctly? I.e., like the admin does.
How can I put the same input type for DateTimeField that I have in the admin interface? (with the Today and Now functions)
The way you have written your view, to display form errors, in your web/error.html template, simply output the errors:
{%if e %}
You had some errors in your submission!<br />
{{ e }}
{% endif %}
However, you don't have explicitly pass the errors list, it is part of the form itself. A bit of simplification:
variables = RequestContext(request, {'form': form})
return render_to_response('web/error.html', variables)
Then, in your template:
{% if form.errors %}
You have some errors!<br />
{{ form.errors }}
{% endif %}
For the second part of your question - to display the django date time widget - things get a bit more involved:
# First, you need to import the widget:
from django.contrib.admin.widgets import AdminSplitDateTime
from django.forms import TextField
# In your form class, you have to specify the widget
# for the field.
class AnimalForm(forms.ModelForm):
pub_date = models.TextField(widget=AdminSplitDateTime)
class Meta:
model = Animal
In order for this to work though, you have to make sure your admin media directory is accessible from your project (since all the javascript and css is included there). You'll also to have make sure that all the stylesheets are also added. It is much easier (and simpler) to use your own javascript form widget from your preferred library.
Finally, as stated in the documentation, if you override any fields, you need to add all the other validation logic yourself:
If you explicitly instantiate a form field like this, Django assumes
that you want to completely define its behavior; therefore, default
attributes (such as max_length or required) are not drawn from the
corresponding model. If you want to maintain the behavior specified in
the model, you must set the relevant arguments explicitly when
declaring the form field.
burhan's answer is spot on. Additionaly, You might probably want to hide the publisher on the form and deal with it in your view.
To do this, add exclude = ('publisher',) to class Meta in your ModelForm.
And then in your view:
if form.is_valid():
animal = form.save(commit=false)
animal.publisher = request.user
animal.save()
Otherwise, as it stands I think your form will show all users in a dropdown, which might not be what you want.