How to change context in Django TemplateView based on url - python

I am stuck with an automation problem in that I have a django project with multiple subdomains, one for each council model instance. All of my models have a foreignfield of 'council' and that is how I am filtering the context of the sites by creating a separate view for each council that I add.
However I would like to somehow create a way to automate adding a view class with the right filtered context each time I add a model instance (Council).
Also I will need to add another url in the urlpatterns which again I am stumped maybe a bit of python could help there but the views are my main concern.
in my views:
class RedmarleyPageView(TemplateView):
template_name = "index.html"
def get_context_data(self, **kwargs):
council_no = 1
context = super (RedmarleyPageView, self).get_context_data (**kwargs)
context['members'] = Member.objects.filter (council=council_no)
context['roles'] = Role.objects.filter (council=council_no)
context['minutes'] = Minute.objects.filter (council=council_no)
context['agendas'] = Agenda.objects.filter (council=council_no)
context['documents'] = Document.objects.filter (council=council_no)
context['articles'] = Article.objects.filter (council=council_no)
context['councils'] = Council.objects.filter (id=council_no)
context['settings'] = Setting.objects.filter(council=council_no)
context['events'] = Event.objects.filter(council=council_no)
context['eventscategories'] = EventCategory.objects.all()
return context
urls:
url(r'^$', HomePageView.as_view(), name='home'),
url(r'^redmarley/', RedmarleyPageView.as_view(), name='redmarley'),
Redmarley in this context is the council inst. and I need it to be able to create a view and url like above for each instance.

Related

How to track ListView, other views and urls via Mixins in Django App

Friends,
I have a Django app and I want to add some basic tracking for all my views. (Much like a counter or something similar)
What I have so far is that I can track specific objects with mixins. So every time someone is clicking on an instance of my model (the DetailView) an entry is added to my database. I did this via the django content types.
Now, to do this I need a get method to actually get a specific object back.
But in my ListView I don't have that object.
How could I implement something similar for either urls or just my ListView? Is that even possible? I'd like to record a single entry stating that the list of my model has been accessed.
Here is what I have so far:
my views
class ListJobView(ObjectViewMixin, ListView):
model = Job
context_object_name = 'jobs'
template_name = 'list_jobs.html'
ordering = '-pub_date'
# paginate_by = 1
class DetailJobView(ObjectViewMixin, DetailView):
model = Job
template_name = 'detail_job.html'
queryset = Job.objects.all()
def get_object(self):
id = self.kwargs.get("id")
return get_object_or_404(Job, id=id)
my mixin
from .signals import object_viewed_signal
class ObjectViewMixin:
def dispatch(self, request, *args, **kwargs):
try:
instance = self.get_object()
except self.model.DoesNotExist:
instance = None
if instance is not None:
object_viewed_signal.send(instance.__class__, instance=instance, request=request)
return super(ObjectViewMixin, self).dispatch(request, *args, **kwargs)
my signal
from django.dispatch import Signal
object_viewed_signal = Signal(providing_args=['instance', 'request'])
here is the signal handler:
def object_viewed_receiver(sender, instance, request, *args, **kwargs):
new_viewed_object = ObjectViewed.objects.create(
user = request.user,
content_type = ContentType.objects.get_for_model(sender),
object_id = instance.id,
)
object_viewed_signal.connect(object_viewed_receiver)
If I should provide more code please let me know.
Any help is highly appreciated...
So this answer is without any guarantees to work. I implemented this solution at the time I was asking the question but I am not using it anymore since I removed this functionality from my django app. A reason for this was the heavy load. Because in this solution I create an object for every time someone visits the website and save it to the DB. This works fine but gets really heavy for the DB after a while.
First I created and new app and called it analytics. In the app I created model like this:
class UrlTime(models.Model):
associated_url = models.CharField(blank=True, null= True, max_length=250)
timestamp = models.DateTimeField(auto_now=True, blank=True)
def __str__(self):
return self.associated_url
I created a new module called middleware.py. A middleware basically executes code either before a view is called or after a view is called (dependent where you put the code). If you count URLs I think it's good to have it before a view is called:
middleware.py
from .models import UrlTime
class GetUrlMiddleware():
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# before view
# creates a counter object for URL if url doesn't exist. Else counts up.
if request.path.startswith('/admin/'): ##exclude what you don't want to count
pass
else:
# urltrack, _ = UrlTrack.objects.get_or_create(url=request.path)
# urltrack.counter = F('counter') + 1
# urltrack.save()
urltime = UrlTime.objects.create(associated_url=request.path)
urltime.save()
response = self.get_response(request)
# after view
return response
With this you should be able to count your URls every time someone visits the page. An object will be created in the DB. Then you only have to display it how you want it on a template. I did something like this:
class AnalyticsIndexView(StaffRequiredMixin, ListView):
template_name = 'analytics_list.html'
model = UrlTime
def get_context_data(self, **kwargs):
context = super(AnalyticsIndexView, self).get_context_data(**kwargs)
context['object_viewed_list'] = ObjectViewed.objects.all()
context['url_views_list'] = UrlTime.objects.all()
context['total_views'] = UrlTime.objects.all().count
counting = UrlTime.objects.all().values('associated_url').annotate(url_count=Count('associated_url'))
context['home_view'] = counting
context['start'] = UrlTime.objects.first()
return context
Then you just need to implement the template.... If you need that too let me know and I post it here too.

Django redirect after search using a queryset

When I click search on the homepage, I want it to take that query set e.g (http://127.0.0.1:8000/?q=car) and then use the same url, but in the search view. I have tried searching around but couldn't find anything that was working.
Views:
class IndexView(ListView):
model = Post
# queryset = Post.objects.filter(live=True)
template_name = "public/index.html"
def get_queryset(self):
queryset = super().get_queryset().filter(live=True)
query = self.request.GET.get("q")
if query:
queryset = queryset.filter(title__icontains=query)
return redirect(reverse('search-view'), queryset)
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
context['queryset'] = self.get_queryset()
context['category'] = Category.objects.all()
return context
URLS:
urlpatterns = [
path('', views.IndexView.as_view(), name="index-view"),
path('post/create/', views.PostCreateView.as_view(), name="post-create"),
path('post/<slug>/update/', views.PostUpdateView.as_view(), name="post-update"),
path('post/<slug>/', views.PostDetailView.as_view(), name="post-detail"),
path('category/', views.CategoryView.as_view(), name="category"),
path('category/<int:pk>/', views.CategoryDetailView.as_view(), name="category-detail"),
path('search/', views.SearchListView.as_view(), name="search-view")
]
I have tried doing it using a redirect and reverse but it is not working at all, it is actually giving me an error for using slice on a forloop that is using queryset. 'slice' object has no attribute 'lower'
I have 2 templates/views. When I click search on the homepage, I want it to carry on over to the search view and then run the search query on there.
Thanks.
I think you're mixing up the logic a bit on where to do the actual query. You should't do the actual query search in the IndexView that is meant for the SearchListView.
From the information that's available right now (without the SearchListView) I'd say you could do a redirect and pass the parameter onto the query url and let the view in SearchListView decide what to do with the information:
def get_queryset(self):
queryset = super().get_queryset().filter(live=True)
query = self.request.GET.get("q")
if query:
# Fetch the url for the search-view
base_url = reverse('search-view')
# Make the search query url encoded
query_string = urlencode({'q': query})
# Tie it together with the url
url = '{}?{}'.format(base_url, query_string)
# Fire away
return redirect(url)
return redirect(reverse('search-view'), queryset)
Source:
Some code taken from The Ultimate Guide to Django Redirects by Daniel Hepper

Add custom page to django admin without a model

I'm trying to add a custom page to the admin without a model association.
This is what I achieved so far.
class MyCustomAdmin(AdminSite):
def get_urls(self):
from django.conf.urls import url
urls = super(MyCustomAdmin, self).get_urls()
urls += [
url(r'^my_custom_view/$', self.admin_view(MyCustomView.as_view()))
]
return urls
class MyCustomView(View):
template_name = 'admin/myapp/views/my_custom_template.html'
def get(self, request):
return render(request, self.template_name, {})
def post(self, request):
# Do something
pass
admin_site = MyCustomAdmin()
admin_site.register(MyModel1)
admin_site.register(MyModel2)
# etc...
This is actually working but the problem is that with this solution I loose some apps from the Django admin interface (account, auth, socialaccounts, sites).
This is because your other admins are using the default admin.site. You need to totally replace the default admin.site with your own as explained here (you may also want to read this too).
Or you can just do it piggy-style by monkeypatching the default admin.site.get_urls() method:
from django.contrib import admin
_admin_site_get_urls = admin.site.get_urls
def get_urls():
from django.conf.urls import url
urls = _admin_site_get_urls()
urls += [
url(r'^my_custom_view/$',
admin.site.admin_view(MyCustomView.as_view()))
]
return urls
admin.site.get_urls = get_urls
Legal disclaimer : I won't be held responsible for any kind of any unwanted side-effect of this "solution", including (but not restricted too) your coworkers defenestrating you on the next code review. It's a dirty solution. It's a mess. It stinks. It's evil. You shouldn't do that, really.

Django - Custom admin action

I'm creating a custom django admin action to show the selected Projects in a chart which I have in a template, the problem that I'm having is that it's displaying all the existing projects and I want just to display the ones which the user selects in the admin part.
Here's the admin.py part which should filter the projects that the user had selected:
def show_gantt_chart_of_selected_projects(modeladmin, request, queryset):
selected = request.POST.getlist(admin.ACTION_CHECKBOX_NAME)
ct = ContentType.objects.get_for_model(queryset.model)
return HttpResponseRedirect("/xxx/?ct=%s&ids=%s" % (ct.pk, ",".join(selected)))
Here's the view.py part which should get the filtered projects:
def index(request):
projects = Project.objects.order_by('projectName') // I think this line could be the problem
context = {'projects': projects }
return render_to_response('xxx/ganttChart.html', context, context_instance=RequestContext(request))
When I open the chart site, the URL show the items that the user selected correctly(ex. http://x.x.x.x:xxxx/xxx/?ct=15&ids=10,1,3,5), but the chart still show all the existing projects.
The queryset parameter to the admin action already contains the selected projects. Alter to:
def show_gantt_chart_of_selected_projects(modeladmin, request, queryset):
ct = ContentType.objects.get_for_model(queryset.model) # why do you do this, you're not using it?
return HttpResponseRedirect("/xxx/?ct=%s&ids=%s" % (ct.pk, ",".join(queryset.values_list('pk', flat=True)))
BTW you should use reverse url resolving instead of hardcoding urls.
View, which I took the liberty to switch to a class-based version. You will want to do this eventually anyway:
from django.views.generic import ListView
class IndexView(ListView):
template_name = 'xxx/ganttChart.html'
context_object_name = 'projects'
model = Project
def get_queryset(self):
return Project.objects.filter(
pk__in=self.request.GET.get('ids','').split(','),
).order_by('projectName')
index = IndexView.as_view()

Functionality of views.py and urls.py in django?

I am a newbie in djnago. I am trying to understand the openstack project. In one place they are calling class AdminIndexView through url in urls.py. I understand how the url works, but in class AdminIndexView there are some methods like def get_data(self):, def has_more_data. I wanted to know where they call these methods. And in urls.py they are using something like url(r'^$', views.AdminIndexView.as_view(), name='index'). What do as_view and name='index' mean?
views.py
class AdminIndexView(tables.DataTableView):
table_class = project_tables.AdminInstancesTable
template_name = 'admin/instances/index.html'
page_title = _("Instances")
def has_more_data(self, table):
return self._more
def get_data(self):
instances = []
marker = self.request.GET.get(
project_tables.AdminInstancesTable._meta.pagination_param, None)
search_opts = self.get_filters({'marker': marker, 'paginate': True})
# Gather our tenants to correlate against IDs
try:
tenants, has_more = api.keystone.tenant_list(self.request)
except Exception:
tenants = []
msg = _('Unable to retrieve instance project information.')
exceptions.handle(self.request, msg)
if 'project' in search_opts:
ten_filter_ids = [t.id for t in tenants
if t.name == search_opts['project']]
del search_opts['project']
if len(ten_filter_ids) > 0:
search_opts['tenant_id'] = ten_filter_ids[0]
else:
self._more = False
return []
urls.py
from django.conf.urls import patterns
from django.conf.urls import url
from openstack_dashboard.dashboards.admin.instances import views
INSTANCES = r'^(?P<instance_id>[^/]+)/%s$'
urlpatterns = patterns(
'openstack_dashboard.dashboards.admin.instances.views',
url(r'^$', views.AdminIndexView.as_view(), name='index'),
In Django you can hook up URL patterns to a handler. These handlers are either functions are can be "class based". In short class based views are good! and .as_view() basically sets up the class to accept requests from a URL pattern.
I suggest you look here for more info on class based views: https://docs.djangoproject.com/en/1.8/topics/class-based-views/.
The name parameter is an arbitrary name you can give to a URL so that you can find it later, for example you if you needed to construct the URL of a certain view instead of creating the url yourself each time, you would call a method.
reverse("<app_name>:<name>")
Where app_name is the name of your application and name is the value in the name parameter (in Django 1.8 + names of urls are name spaced by app_name)
As for the get_data methods, these are called by the superclass of your view see here for more info on that:
http://docs.openstack.org/developer/horizon/topics/tables.html#fetching-the-row-data

Categories

Resources