I'm setting up Django to use a different template for mobile but I'm not sure how to set it up for Class based views. If I include it in the class like below it throws an error.
class EventList(ListView):
model = Event
paginate_by = 10
context_object_name = 'events'
category = None
area = None
starts = None
ends = None
slug_level = ""
if request.mobile:
template_name = "mobile/mobile.html"
...
I have functions like def get_queryset(self): where to place it so it uses a different template for mobile since request isn't in the class based view
I'm using minidetector in function like so:
#detect_mobile
def home(request, template_name='pages/home.html'):
....
if request.mobile:
return render_to_response('mobile/mobile.html', context)
Mobile and desktop will generally use the same HTTP methods, so CBV is not where you distnguish between these clients.
Instead, you should look at the client's properties such as the user-agent header and set the template name accordingly.
I think the following page gives a great intro:
http://mobiforge.com/design-development/build-a-mobile-and-desktop-friendly-application-django-15-minutes
Related
What I need
I'm developing a Pull Notification System to an existing Django Project. With there begin over 100+ views I'm looking to find a way to incorporate a argument(notification queryset) into all the views, as this is rendered to my base.html which I can do by passing it into a view's arguments dictionary.
Problem
I want to do this without editing all of the views as this would take a long time and would be required for all future views to include this variable.
What I've tried
Creating a template filter and pass in request.user as variable to return notification for that user. This works, however when the user selects a 'New' notification I want to send a signal back to the server to both redirect them to the notification link and change the status of viewed to 'True' (POST or AJAX). Which would required that specific view to know how to handle that particular request.
What I've considered
• Editing the very core 'View' in Django.
I've tried my best to explain the issue, but if further info is required feel free to ask.
Thanks!
models.py
class NotificationModel(models.Model):
NOTIFICATION_TYPES = {'Red flag':'Red flag'}
NOTIFICATION_TYPES = dict([(key, key) for key, value in NOTIFICATION_TYPES.items()]).items()
notification_type = models.CharField(max_length=50, choices=NOTIFICATION_TYPES, default='')
user = models.ForeignKey(User, on_delete=models.CASCADE)
created = models.DateTimeField(blank=True, null=True)
text = models.CharField(max_length=500, default='')
viewed = models.BooleanField(default=False)
views.py Example
class HomeView(TemplateView):
template_name = 'app/template.html'
def get(self, request, *args, **kwargs):
notifications = NotificationsModel.objects.filter(user=request.user)
args={'notifications':notifications}
return render(request, self.template_name, args)
def notification_handler(self, request)
if 'notification_select' in request.POST:
notification_id = request.POST['notification_id']
notification = NotificationModel.objects.get(id=notification_id)
notification.viewed = True
notification.save()
return redirect #some_url based on conditions
def post(self, request, *args, **kwargs):
notifications = self.notification_handler(request, *args, **kwargs)
if notifications:
return notifications
return self.get(self, request)
With there begin over 100+ views I'm looking to find a way to incorporate a argument(notification queryset) into all the views, as this is rendered to my base.html which I can do by passing it into a view's arguments dictionary.
You don't have to put it in the views (and actually, you shouldn't - views shouldn't have to deal with unrelated responsabilities) - you can just write a simple custom template tag to fetch and render whatever you need in your base template. That's the proper "django-esque" solution FWIW as it leaves your views totally decoupled from this feature.
Have you considered mixins ?
class NotificationMixin:
my_var = 'whatever'
class MyDjangoView(NotificationMixin,.....,View)
pass
Better, Using django builtins..
from django.views.generic import base
class NotificationMixin(base.ContextMixin):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['notification'] = 'whatever'
return context
and use this mixin with all your views AS THE FIRST CLASS INHERITED, See this.
For your case, Typing it instead of applying it to the base class is better, The base view class is not intended to be altered, It's meant to be extended, That's why we can solve the issue by this...
from django.generic.views import View as DjangoBaseView
from .mixins import NotificationMixin
class View(NotificationMixin, DjangoBaseView):
pass
and use any IDE to change all the imported Views to your View and not the django one.
I am playing with Wagtail 2.6.1 and I got in confusing problem. I need to work with Page model in vanilla Django view. I want to get children of BlogPage, so I made custom method, which should return all children pages. Method works, but only in template view. When I access it in django view, it returns empty queryset. I really don't understand why.
My models.py
class BlogPage(Page):
template = "blog/blog.html"
max_count = 1
subpage_types = ['blog.BlogPostPage','blog.PostAdvancedPage']
promote_panels = Page.promote_panels + [
FieldPanel('menu_order'),
]
def getChildren(self):
children = self.get_children().live().all()
return children
def get_context(self, request):
context = super(BlogPage, self).get_context(request)
context['categories'] = BlogCategory.objects.order_by('name').all()
context['blog_posts'] = self.get_children().live().order_by('-first_published_at').all()
return context
class Meta:
verbose_name = "Blog"
verbose_name_plural = "Blogs"
my views.py
from .models import BlogPage
def post_filter(request):
if request.method == 'POST':
posts = BlogPage().getChildren() # this return empty queryset
print (posts)
but when I render this method in template blog.html it works:
<div class="section py-9">
{{self.getChildren}}
</div>
it successfully render all children pages in template:
<PageQuerySet [<Page: Lorem Ipsum 01>,<Page: Lorem Ipsum 02>]>
Please note that I am normally using get_context method to render all posts in my template, I just tried render this method (getChildren) in template to check if that's working.
The line BlogPage().getChildren() means "create a new BlogPage instance in memory, and fetch its children". Naturally, since it's only just been created, it won't have any child pages, and so this returns an empty queryset.
For the post_filter view to do something useful, you'll need to specify an existing BlogPage instance for it to call getChildren on. You haven't mentioned how post_filter is going to be used, so I don't know where this information should come from - but one possibility is to pass it as a parameter in the URL, in which case post_filter will look like this:
def post_filter(request, page_id):
if request.method == 'POST':
posts = BlogPage.objects.get(id=page_id).getChildren()
print (posts)
I have a Django generic List View that I want to filter based on the value entered into the URL.
For example, when someone enters mysite.com/defaults/41 I want the view to filter all of the values matching 41.
I have come accross a few ways of doing this with function based views, but not class based Django views.
I have tried:
views.py
class DefaultsListView(LoginRequiredMixin,ListView):
model = models.DefaultDMLSProcessParams
template_name = 'defaults_list.html'
login_url = 'login'
def get_queryset(self):
return models.DefaultDMLSProcessParams.objects.filter(device=self.kwargs[device])
urls.py
path('<int:device>', DefaultsListView.as_view(), name='Default_Listview'),
You are close, the self.kwargs is a dictionary that maps strings to the corresponding value extracted from the URL, so you need to use a string that contains 'device' here:
class DefaultsListView(LoginRequiredMixin,ListView):
model = models.DefaultDMLSProcessParams
template_name = 'defaults_list.html'
login_url = 'login'
def get_queryset(self):
return models.DefaultDMLSProcessParams.objects.filter(
device_id=self.kwargs['device']
)
It is probably better to use devide_id here, since then it is syntactically clear that we compare identifiers with identifiers.
It might also be more "idiomatic" to make a super() call, such that if you later add mixins, these can "pre-process" the get_queryset call:
class DefaultsListView(LoginRequiredMixin,ListView):
model = models.DefaultDMLSProcessParams
template_name = 'defaults_list.html'
login_url = 'login'
def get_queryset(self):
return super(DefaultsListView, self).get_queryset().filter(
device_id=self.kwargs['device']
)
I have a django listview working fine.
Its receive url parameters to filter data.
Its paginated.
Now, I want to maintain these data along the user session. (page number and url parameters).
Example:
I'm in the products list view.
I search for 'foo'
I select page 2
And then, I click in any product detail.
The page will redirect to detail view.
When I return to product list view, I whant to keep the search argument 'foo' and selected page 2.
What is the better way to do this?
I'm using Django 2.0.6
Models.py
class Product(models.Model):
name= models.CharField(_('name'), max_length=150)
price = models.DecimalField(max_digits=10, decimal_places=2, default=0.0)
Views.py
class ProductList(ListView):
model = Product
paginated_by = 10
def get_queryset(self):
queryset = Product.objects.all()
name = self.request.GET.get('name', None)
if name:
queryset = queryset.filter(name__icontains=name)
return queryset
Urls.py
path('products/', views.ProductList.as_view(), name='product_list'),
For this you have to put the URL as a get request so that you can fetch the get values form the URL and use them in your filter to maintain your selection like:
url/?variable=value
Then in your Django view, you can access this by request.GET.get('variable') and pass this as the context in your HTML render page then use that variable in your filter selection.
Setting variable in session:
For setting the variable in the session you can set it by:
request.session['variable'] = 'value'
and this value can be retrieve by:
if 'variable' in request.session:
variable1 = request.session['variable']
You can refer this docs.
One common trick I use to do this is to use GET parameters and save directly the entire url in session (it saves time compared to saving each individual parameter individually)
class ProductList(ListView):
model = Product
paginated_by = 10
def get_queryset(self):
self.request.session['saved_product_list_url'] = self.request.get_full_path()
....
Then you can use it like this in templates :
product list
Or like this in views :
saved_product_list_url = self.request.session.get('saved_product_list_url')
if saved_product_list_url:
return redirect(saved_product_list_url)
else:
return redirect('product_list')
Also in your filter form you should add a "reset filters" like this :
reset filters
I have a search form in the navbar that is obviously available site wide, however, search only works on the home page because thats the view the search code is written. I figured class based views were the answer such that I could easily add the search functionality as a mixin or something of the kind.
Bottomline: I'm having trouble abstracting the search functionality in a class based DRY
The search form returns www.site.com/?q=search. I need to grab q from the GET request and return that info to my search.html page.
This is my code as it stands
views.py
class HomeListView(ListView, Searchmixin):
model = Part
queryset = Part.objects.order_by('-creDate')
template_name = 'parts/index.html'
paginate_by = 20
is_paginated = True
search.py
class SearchMixin(object):
def sindex(self):
query = self.request.GET.get('q')
return httpResponse(query)
def perf(self, query):
if query is not None:
pollist = self.objects.filter(
Q(project__name__icontains=query) |
Q(owner__icontains=query) |
Q(description__icontains=query) |
Q(pnumber__icontains=query)
)
return render(request, 'parts/search.html', pollist)
Nice job creating your first site. On every view that you would like to have a functional search bar, you should include the SearchMixin that you've created.
One important thing that will save you some trouble down the road is to make sure that the generic class-based views go on the right-hand side of any mixins that you've created -- i.e. class HomeListView(Searchmixin, ListView). That's because Python has an order in which it builds up the class based on what it's inheriting from. Sometimes there could be conflicts if the default ones get overwritten before they're supposed to.
Instead of using SearchMixin and inheriting everywhere, I suggest creating a SearchView, assigning it some URL and returning search results from there.
# urls.py
urlpatterns = [
url(r'/search/', SearchView.as_view(), name='search'),
...
]
# views.py
class SearchView(ListView):
paginate_by = 20 # to show 20 search results per page
def get_template_names(self):
return ['parts/search.html']
def get_queryset(self):
query = self.request.GET.get('q')
pollist = YourModel.objects.all()
if query:
pollist = pollist.filter(
Q(project__name__icontains=query) |
Q(owner__icontains=query) |
Q(description__icontains=query) |
Q(pnumber__icontains=query)
)
return pollist
Now in your JavaScript, you can perform asynchronous GET request to http://www.your_site.com/search/?q=some_search_term, and fetch results and append them in DOM. Hope this helps.