Django - Custom admin action - python

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()

Related

Number of Post Views is not showing in template ( Function Based Views )

I am Building a BlogApp and I implement a feature to count the views of Post. BUT views are not showing in post page in Browser.
What i am trying to do
I am trying to count the number of views which post will got whenever the user visits.
The Problem
Post views are not showing in post page in browser.
What have i tried
1). I also tried models.IntegerField BUT that didn't worked for me , because whenever user refresh the page then it increase one view everytime for single user.
2). I followed some tutorials BUT the all of them were on Class Based Views and I am using Function Based Views.
3). Then i thought of IP address BUT that also didn't worked for me because it wasn't working for my server.
Then i think of a ManyToManyField in views variable. BUT it also not working for me, I don't know where is the problem.
views.py
def detail_view(request,pk):
data = get_object_or_404(BlogPost,pk=pk)
queryset = BlogPost.objects.order_by('viewers')
context = {'queryset':queryset,'data':data}
return render(request, 'mains/show_more.html', context )
models.py
class BloPost(models.Model):
post_owner = models.ForeignKey(User,default='',null=True,on_delete = models.CASCADE)
date_added = models
viewers = models.ManyToManyField(settings.AUTH_USER_MODEL,related_name='viewed_posts',editable=False)
show_more.html
1). This is showing auth.User.None
{{ data.viewers }}
2). This is showing all the posts.
{{ queryset }}
I don't what am i doing wrong.
Any help would be appreciated.
Thank You in Advance.
You should annotate your queryset, so:
from django.db.models import Count
def detail_view(request,pk):
queryset = BlogPost.objects.annotate(
num_views=Count('viewers')
).order_by('-num_views')
data = get_object_or_404(queryset, pk=pk)
context = {'queryset':queryset,'data':data}
return render(request, 'mains/show_more.html', context )
and then you can render this with:
{{ data.num_views }}
If you want to add the user to the viewers, you can run the logic to add the user to the viewers:
from django.db.models import Count
def detail_view(request,pk):
queryset = BlogPost.objects.annotate(
num_views=Count('viewers')
).order_by('-num_views')
data = get_object_or_404(queryset, pk=pk)
if self.user.is_authenticated:
__, created = Post.viewers.through.objects.get_or_create(
post=data,
user=self.request.user
)
if created:
data.num_views += 1
context = {'queryset':queryset,'data':data}
return render(request, 'mains/show_more.html', context )
That being said, this to some extent demonstrates that function-based views are often not well-suited for a view that requires to run logic that consists out of a number of separate parts. In that case it is better to work with mixins and define a class-based view in terms of these mixins.

can't adapt type 'SimpleLazyObject' with get_context_data (Class Based View)

I have a Django web app deployed to Heroku.
App is deployed and working well, except for the related issue.
Some specs :
In my local env I use SQLite 3 DB
In Heroku env I use Postgress DB
When I try to render a class based view this error happens to me:
can't adapt type 'SimpleLazyObject'
After some checks about this issue I suspect it is related to the User object. but i don't know how to approach it.
View Code:
class ProfileListView(LoginRequiredMixin, ListView):
model = Profile
template_name = 'profiles/profile_list.html'
context_object_name = 'qs'
def get_queryset(self):
qs = Profile.objects.get_all_profiles(self.request.user)
return qs
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
return context
URL :
urlpatterns = [
path('', ProfileListView.as_view(), name='all-profiles-view'),
]
Customize manager:
class ProfileManager(models.Manager):
def get_all_profiles(self, me):
profiles = Profile.objects.all().exclude(user=me)
return profiles
EDIT:
It likely seems the source of the problem is related to :
user = User.objects.get(username__iexact=self.request.user)
The problem seems from get_queryset. Because django adds a user attribute to request that is an instance of SimpleLazyObject. Since your error traceback shows it was problem with SimpleLazyObject, I suspect the problem comes from request.user could be either an object of User or anonymous user. So, it the following answer, I tried to add if statement to make sure that only the authenticated user will have qs.
You can tweak it a bit for your needs.
try replace with
def get_queryset(self):
qs= []
if self.request.user.is_authenticated():
qs = Profile.objects.get_all_profiles(self.request.user)
return qs

Django - how to bulk delete

I'm working in a CRUD and one of the features of the list is bulk delete. The user chooses (checkbox) the lines he wants to delete and press the "Delete selected" button.
I'm using generic views in my CRUD. My single delete, for example is like this:
class ContentDeleteView(NextRedirectMixin, DeleteView):
model = Content
template_name = 'content_delete.html'
def get_success_url(self):
messages.add_message(self.request, messages.SUCCESS, "Content deleted successfully.")
return reverse('content:content-detail')
My problem is creating the bulk delete view. Is there something from Django core features that I could use to bulk delete? I would like to avoid installing an app for that.
Thanks for any help
You should resort to rolling out your own kind of delete view, here is a basic example:
class BulkDeleteView(View):
model = None
def post(self, request, *args, **kwargs):
delete_ids = request.POST['delete_ids'].split(',') # should validate
self.model.objects.filter(pk__in=delete_ids).delete()
return render / redirect
So the basic idea is to subclass View and roll out your own implementation of a base BulkDeleteView

merging a view with template view django

I want that the landing page of my homepage is a form with an input and the user puts in stuff. So I followed a couple of tutorials and now I have this:
views.py:
def create2(request):
if request.method =='POST':
form = LocationForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('')
else:
form = LocationForm()
args = {}
args.update(csrf(request))
args['form'] = form
return render_to_response('location/index.html', args)
and in my urls.py:
url(r'^$', 'core.views.create2'),
which works perfectly fine, if I go to 127.0.0.1:8000 I get to index.html and when put in something in the input it gets saved in the database. However, the old part of my homepage looks like this
class LandingView(TemplateView):
model = Location
template_name="location/index.html"
def search
...
and the urls.py:
url(r'^$', core.views.LandingView.as_view(), name='index'),
which has a function search I So my question is, is there a way how I can merge the def create2 into my LandingView. I tried several things, but I am always ending up having the index.html without the input field. I also tried
def create2
...
def search
...
but didn't work.
Does anyone know how to merge that together?
EDIT
Thank you the working solution looks like this now
class Create(CreateView):
model = coremodels.Location
template_name = 'location/test.html'
fields = ['title']
def form_valid(self, form):
form.save()
return HttpResponseRedirect('')
return super(Create, self).form_valid(form)
Depending on the results you are looking for, there are multiple ways to solve this:
1. Use CreateView and UpdateView
Django already provides some classes that render a form for your model, submit it using POST, and re-render the form with errors if form validation was not successful.
Check the generic editing views documentation.
2. Override get_context_data
In LandingView, override TemplateView's get_context_data method, so that your context includes the form you are creating in create2.
3. Use FormView
If you still want to use your own defined form instead of the model form that CreateView and UpdateView generate for you, you can use FormView, which is pretty much the same as TemplateView except it also handles your form submission/errors automatically.
In any case, you can keep your search function inside the class-based view and call it from get_context_data to include its results in the template's context.

How to render an html template with data from view?

I am new to django. I made a form. I want that if the form is filled successfully then django should redirect to a success page showing the name entered in the form but no parameters should be present in the url itself.
I searched on the internet and the solution I got was to redirect to url with pk as a get parameter which fetches the data and shows in the view. But I don't want to pass any thing in the url itself. and some websites say that http can't redirect with post data.
Here's my views.py
class UserRegistrationView(CreateView):
model = UserForm
template_name = 'userregistration.html'
form_class = UserForm
success_url = 'success'
def get_success_url(self):
return reverse('success',kwargs = {'name' : self.object.firstName})
and here's the template to which I want to redirect:
<h2>Congratualations for registering {{name}} </h2>
Basically what I want is that if the person fill form mentioning his/her firstName as "xyz" then the redirected success page should say that "Congratulations for registering xyz"
You can use django sessions, which I believe installed by default in 1.8
Look here
# Set a session value:
request.session["fav_color"] = "blue"
# Get a session value -- this could be called in a different view,
# or many requests later (or both):
fav_color = request.session["fav_color"]
# Clear an item from the session:
del request.session["fav_color"]
You can pass your pk via session and extract your object in another view without affecting your url.
Make sure you clean up after yourself.
Let me know if more help needed.
One of the possible ways of passing data between views is via sessions. So, in your UserRegistrationView you need to override the form_valid method as shown below.
class UserRegsitrationView(CreateView):
def form_valid(self,form):
self.request.session['name'] = self.object.firstName
return super(UserRegistrationView,self).form_valid(form)
class SuccessView(TemplateView):
template_name = "success_template.html"
def get_context_data(self,**kwargs):
context = super(SuccessView,self).get_context_data(**kwargs)
context['name'] = self.request.session.get('name')
del self.request.session['name']
return context
One more thing that you can modify in your code is that you need not declare success_url if you are overriding get_success_url

Categories

Resources