Closed mitchdowney closed 11 years ago
A route should be set up that provides this modified election view:
/election/:e_id/:o_id
where :e_id is the election id and :o_id is the office id.
Down the road we can support prettier urls
I'd like to learn more about how this would be done.
The current urls.py for showing the election page is:
# SHOW ELECTION - RANDOM CANDIDATES
url(r'^elections/(?P<pk>\d+)/$', ElectionDetailView.as_view(
model=Election,
template_name='election_detail.html',
context_object_name='election'),
name='election_detail',
),
And the views is just (minus your blocked_users function):
class ElectionDetailView(DetailView):
model = Election
context_object_name = "election"
I don't know how to type the urls.py so that it accepts a pk for the election, and a different pk for the office. Maybe something with changing slug_field?
There are many ways to achieve this. The cleanest way to do this is to implement the query logic in models and override the get_queryset within the DetailView.
Within models.py
add a method to Election
class to accept a candidate id and office id and return the filtered query set. Just filtering the candidate_set based on office should work. You can debug this in your shell or writes tests to ensure it works.
Next, add a new DetailView in views.py
(possibly mapped with the url regex url(r'^elections/(?P<pk>\d+)/office/(?P<office>\d+)/$',
) and override the get_queryset
as mentioned in the docs.
Thank you arocks!
Well I tried for a few hours with your suggestion, and I can see you have shown the right answer, but I don't understand yet how to use get_queryset. The example in the docs is a ListView (I am using DetailView for the election page that lists its candidates on the page), and is not being passed a def from models.py. I might be too much of a noob to understand this right now.
I'm going to read the docs more closely over the next 2 days so I better understand get_queryset. Thanks again for pointing me in the right direction
Notice that Office and Election each have a ForeignKey relationship with Constituency. This is because an Office is a position within a Constituency, but an Office is not within an Election.
Also, I insert candidates on an ElectionDetailView page by running a for loop on election.candidate_set.all. What I need to resolve this issue is basically run election.candidate_set, but filter it to return only candidates that are running for a specific office (for example, only show all President candidates who are running in this specific election).
urls.py
url(r'^elections/(?P<pk>\d+)/office/(?P<office_id>\d+)/$', ElectionOfficeDetailView.as_view(
model=Election,
template_name='election_detail_by_office.html'),
name='election_detail_by_office',
),
models.py
class Constituency(models.Model):
name = models.CharField(max_length=100)
class Office(models.Model):
constituency = models.ForeignKey(Constituency)
name = models.CharField(max_length=30)
class Election(models.Model):
constituency = models.ForeignKey(Constituency)
name = models.CharField(max_length=50)
offices = models.ManyToManyField(Office, blank=True, null=True)
def get_office_candidates(self, office_id):
office_candidates = self.candidate_set.filter(office_id=office_id)
return office_candidates
class Candidate(models.Model):
user = models.ForeignKey(UserProfile)
election = models.ForeignKey(Election)
office = models.ForeignKey(Office)
views.py
class ElectionOfficeDetailView(DetailView):
model = Election
context_object_name = "election"
def get_queryset(self):
???
???
Is the office_candidates function in models.py correct?
My biggest problem is I am confused about how I should override get_queryset to use get_office_candidates() in the views.py, I am not sure how to apply the example given in the docs to solve this problem:
example def get_queryset from docs
class PublisherBookList(ListView):
template_name = 'books/books_by_publisher.html'
def get_queryset(self):
self.publisher = get_object_or_404(Publisher, name=self.args[0])
return Book.objects.filter(publisher=self.publisher)
I am sorry for my ignorance. This is the last and biggest issue to resolve for this app to be ready for user testing. Do you have any suggestions for how to solve this?
get_queryset
is useful only if you want DetailView
to limit its choices of Election
objects.
What you want to do is add a queryset of Candidate
objects to your context
so that the template can use it.
class ElectionOfficeDetailView(DetailView):
model = Election
context_object_name = "election"
def get_context_data(self, **kwargs):
context = super(ElectionOfficeDetailView, self).get_context_data(**kwargs)
# At this point, the context contains the 'election' object
election = context['election']
# Add the 'office_candidates' to the context and use in the
# template.
# {% for candidate in office_candidates %}
# candidate
# {% endfor %}
context['office_candidates'] = election.get_office_candidates(kwargs['office_id'])
return context
Wow, thank you Suriya. Your explanation is very clear, but I tried it and am receiving this error:
KeyError at /elections/1/office/1 ...in get_context_data
Can you help me understand why that error is happening? I apologize, I have only been coding since this July, so there are many things I don't understand...
urls.py
url(r'^elections/(?P<pk>\d+)/office/(?P<office_id>\d+)/$', ElectionOfficeDetailView.as_view(
model=Election,
template_name='election_detail_by_office.html',
context_object_name='election'),
name='election_detail_by_office',
),
models.py
class Election(models.Model):
constituency = models.ForeignKey(Constituency)
name = models.CharField(max_length=50)
offices = models.ManyToManyField(Office, blank=True, null=True)
def get_office_candidates(self, office_id):
office_candidates = self.candidate_set.filter(office_id=office_id)
return office_candidates
and I used exactly your code for views.py
Thank you very much again. This issue has been vexing me for a while.
There needs to a minor change.
--- a.py 2013-09-06 11:48:01.555280223 +0530
+++ b.py 2013-09-06 13:33:10.901188387 +0530
@@ -11,5 +11,5 @@
# {% for candidate in office_candidates %}
# candidate
# {% endfor %}
- context['office_candidates'] = election.get_office_candidates(kwargs['office_id'])
+ context['office_candidates'] = election.get_office_candidates(self.kwargs['office_id'])
return context
Here's the correct version (I hope)
class ElectionOfficeDetailView(DetailView):
model = Election
context_object_name = "election"
def get_context_data(self, **kwargs):
context = super(ElectionOfficeDetailView, self).get_context_data(**kwargs)
# At this point, the context contains the 'election' object
election = context['election']
# Add the 'office_candidates' to the context and use in the
# template.
# {% for candidate in office_candidates %}
# candidate
# {% endfor %}
context['office_candidates'] = election.get_office_candidates(self.kwargs['office_id'])
return context
It works, amazing! Thank you Suriya, this is a huge help. I was stumped on this for a few days.
Please scroll to the last comments to understand this issue.