How do I get django url parameters from a view mixin? - django

Exactly what the title says. I have a mixin that needs to pull in the id of a model field in order to be useful. I assume the easy way to do that would be to pull it from the URL.
class StatsMixin(ContextMixin):
def get_stats_list(self, **kwargs):
# the ??? is the problem.
return Stats.objects.filter(id=???).select_related('url')
def get_context_data(self, **kwargs):
kwargs['stats'] = self.get_stats_list()[0]
print kwargs
return super(StatsMixin, self).get_context_data(**kwargs)
Here's the view implementation for reference.
class ResourceDetail(generic.DetailView, StatsMixin):
model = Submissions
template_name = 'url_list.html'
queryset = Rating.objects.all()
queryset = queryset.select_related('url')

You can access URL parameters in Django by using, self.args and self.kwargs.

Related

Django - Filtering Field In DetailView

Hi all,
I've been building a Django app that allows users to stream and download music. However, there is one issue that I'm having with the artists profile pages; I'm trying to request the songs by the artist only in a DetailView as I'm treating it like a blog system.
Is this possible in a DetailView? Or do I need to make a filter? I've been searching the web for days now and didn't really understand what I can do or how to get the specific data field from the model.
Any help or guidance would be highly appreciated!
class musicartist(DetailView):
model = MusicArtist
template_name = 'RS_MUSIC/artist.html'
# override context data
def get_context_data(self, *args, **kwargs):
context = super(musicartist, self).get_context_data(*args, **kwargs)
# add extra field
current_band = MusicItems.objects.all().filter(artist=MusicArtist.title)[:1]
context["songs"] = MusicItems.objects.filter(artist=MusicArtist.objects.all().filter(title=current_band)[:1])
return context
Managed to figure it out. Just needed the following code:
class musicartist(DetailView):
model = MusicArtist
template_name = 'RS_MUSIC/artist.html'
def get_context_data(self, **kwargs):
context = super(musicartist, self).get_context_data(**kwargs)
context_related = MusicItems.objects.filter(artist=self.object.title)
context['related'] = context_related
return context

Filter model based of context data using django filter

I have a detail view of a model that I want to display together with a list of products and Im trying to integrate django_filter within this view.
Error Message
TypeError at /collections/christmas/?category=mens,
get context data takes exactly 2 arguments (1 given)
Filter
class ProductFilter(django_filters.FilterSet):
class Meta:
model = Product
fields = ['categories']
Detail View
class collection_detail(DetailView):
model = Collection
def get_context_data(self, request, **kwargs):
context = super(collection_detail, self).get_context_data(**kwargs)
context['collection_list'] = ProductFilter(request.GET, queryset=Product.objects.filter(collection=self.object.id).filter(structure='parent'))
return context
How i intend it to work
The category filter is pre-defined in a list of links, so when the user clicks on a collection related to a specific category the url of the detail view would look like /collections/christmas/?category=mens .
What am I specifically doing wrong here? Is it possible to even do this?
You're missing *args in your overridden method.
class collection_detail(DetailView):
model = Collection
def get_context_data(self, *args, **kwargs):
context = super(collection_detail, self).get_context_data(*args, **kwargs)
context['collection_list'] = ProductFilter(self.request.GET, queryset=Product.objects.filter(collection=self.object.id).filter(structure='parent'))
return context

Django CreateView validate field not in fields

So, I have a Django generic view:
class Foobaz(models.Model):
name = models.CharField(max_length=140)
organisation = models.ForeignKey(Organisation)
class FoobazForm(forms.ModelForm):
class Meta:
model = Foobaz
fields = ('name')
class FoobazCreate(CreateView):
form_class = FoobazForm
#login_required
def dispatch(self, *args, **kwargs):
return super(FoobazCreate, self).dispatch(*args, **kwargs)
What I'm trying to do is to take the organisation id from the URL:
/organisation/1/foobaz/create/
And add it back to the created object. I realise I can do this in CreateView.form_valid(), but from what I understand this is then completely unvalidated.
I've tried adding it to get_form_kwargs() but this does not expect the organisation kwarg as it is not in the included fields.
Ideally what I'd like to do is to add it to the instance of the form to validate it with the rest - ensuring it is a valid organisation, and that the user in question has the correct permissions to add a new foobaz to it.
I'm happy to just roll my own view if that is the best way of doing this, but I may just be simply missing a trick.
Thanks!
I thnik it would be better to include the organisation field and define it as hidden and readonly, this way django will validate it for you.
You can then override get_queryset method like this:
def get_queryset(self):
return Foobaz.objects.filter(
organisation__id=self.kwargs['organisation_id'])
where organisation_id is a keyword in url pattern.
You can do it overriding the View's get_kwargs() method and the Form's save() method. In the get_kwargs() I "inject" the organization_id into the initial data of the form, and in save() I retrieve the missing info with the supplied initial data:
In urls.py:
urlpatterns('',
#... Capture the organization_id
url(r'^/organisation/(?P<organization_id>\d+)/foobaz/create/',
FoobazCreate.as_view()),
#...
)
In views.py:
class FoobazCreate(CreateView):
# Override get_kwargs() so you can pass
# extra info to the form (via 'initial')
# ...(all your other code remains the same)
def get_form_kwargs(self):
# get CreateView kwargs
kw = super(CreateComment, self).get_form_kwargs()
# Add any kwargs you need:
kw['initial']['organiztion_id'] = self.kwargs['organization_id']
# Or, altenatively, pass any View kwarg to the Form:
# kw['initial'].update(self.kwargs)
return kw
In forms.py:
class FoobazForm(forms.ModelForm):
# Override save() so that you can add any
# missing field in the form to the model
# ...(Idem)
def save(self, commit=True):
org_id = self.initial['organization_id']
self.instance.organization = Organization.objects.get(pk=org_id)
return super(FoobazForm, self).save(commit=commit)

How to render multiple objects using class based views

I try to render multiple objects using class based views but I get an error.
Here are my codes:
class AssociatedList(WizardRequiredMixin, TemplateView):
template_name = "profile/associated_accounts.html"
def get_context_data(self, **kwargs):
context = super(AssociatedList, self).get_context_data(**context)
all_envelopes = Envelope.objects.filter(
user=request.user).exclude_unallocate()
free_limit = account_limit(request, 15, all_envelopes)
facebook = FacebookProfile.user_profiles(request.user)
google = GoogleProfile.user_profiles(request.user)
twitter = TwitterProfile.user_profiles(request.user)
context.update = ({
'facebook': facebook,
'google': google,
'twitter': twitter,
'free_limit': free_limit,
})
return context
Error:
local variable 'context' referenced before assignment
I've always overridden get_context_data by calling super at the beginning of the function and then appending context -
def get_context_data(self, *args, **kwargs):
context = super(AssociatedList, self).get_context_data(*args, **kwargs)
all_envelopes = Envelope.objects.filter(
user=self.request.user).exclude_unallocate()
free_limit = account_limit(self.request, 15, all_envelopes),
facebook = FacebookProfile.user_profiles(self.request.user),
google = GoogleProfile.user_profiles(self.request.user),
twitter = TwitterProfile.user_profiles(self.request.user),
context.update({
'facebook': facebook,
'google': google,
'twitter': twitter,
'free_limit': free_limit,
})
return context
This is the pattern used in the docs here.
UPDATE
The error you've just added suggests an error with your class. It sounds like you need to define either a queryset attribute or a model attribute.
The ListView class that you're inheriting from requires you to either define the model that the View returns (ie YourModel.objects.all()). Or else the specific queryset to be returned (eg YourModel.objects.filter(your_field=some_variable)).
Because this is a ListView, you need to tell it what you are going to list with either a model or queryset. You don't want to use a ListView in this case since you are overriding get_context_data so you should probably use a TemplateView or something similar.
Try something like this:
class AssociatedList(WizardRequiredMixin, ListView):
template_name = "profile/associated_accounts.html"
model = Envelope
def get_queryset(self):
return Envelope.objects.filter(user=self.request.user).exclude_unallocate()
def get_context_data(self, **kwargs):
context = super(AssociatedList, self).get_context_data(**kwargs)
context.update({
'facebook': FacebookProfile.user_profiles(self.request.user),
'google': GoogleProfile.user_profiles(self.request.user),
'twitter': TwitterProfile.user_profiles(self.request.user),
'free_limit': account_limit(self.request, 15, context['envelope_list']),
})
return context
You don't need model having queryset, but it is good practice to define it.
In template use object_list or envelope_list instead of all_envelopes and you should be good to go.
P.S. http://ccbv.co.uk/ good source of knowledge about CBV.

listing object with specific tag using django_taggit

I have a blog app that uses django_taggit. My HomePageView subclasses ArchiveIndexView and works well.
Now I'd like the following link to work: http://mysite.com/tag/yellow and I'd like to use the ArchiveIndexView generic class and pass in a modified queryset that filters on tag_slug. I want to do this because I want to use the same template as the homepage.
My urls.py is
url(r'^$', HomePageView.as_view(paginate_by=5, date_field='pub_date',template_name='homepage.html'),
),
url(r'^tag/(?P<tag_slug>[-\w]+)/$', 'tag_view'), # I know this is wrong
My views.py is
class HomePageView(ArchiveIndexView):
"""Extends the detail view to add Events to the context"""
model = Entry
def get_context_data(self, **kwargs):
context = super(HomePageView, self).get_context_data(**kwargs)
context['events'] = Event.objects.filter(end_time__gte=datetime.datetime.now()
).order_by('start_time')[:5]
context['comments'] = Comment.objects.filter(allow=True).order_by('created').reverse()[:4]
return context
I realize I'm lost here, and would like some help in finding out how to create a new class TagViewPage() that modifies the queryset by filtering on tag_slug.
The key thing is to override the get_queryset method, so that the queryset only includes returns entries with the chosen tag. I have made TagListView inherit from HomePageView, so that it includes the same context data - if that's not important, you could subclass ArchiveIndexView instead.
class TagListView(HomePageView):
"""
Archive view for a given tag
"""
# It probably makes more sense to set date_field here than in the url config
# Ideally, set it in the parent HomePageView class instead of here.
date_field = 'pub_date'
def get_queryset(self):
"""
Only include entries tagged with the selected tag
"""
return Entry.objects.filter(tags__name=self.kwargs['tag_slug'])
def get_context_data(self, **kwargs):
"""
Include the tag in the context
"""
context_data = super(TagListView, self).get_context_data(self, **kwargs)
context_data['tag'] = get_object_or_404(Tag, slug=self.kwargs['tag_slug'])
return context_data
# urls.py
url(r'^tag/(?P<tag_slug>[-\w]+)/$', TagListView.as_view(paginate_by=5, template_name='homepage.html')),