Django-tables2 - self.request in render - django

I am trying to change the table view based on the username of requests, but it keeps returning the AttributeError for the table object has no attribute 'request.' I have directly added the request to the table (please refer below for codes), but still not able to get it done. In my table class, I have the "get_top_pinned_data" overridden to fetch the data from the database based on the username.
views.py
class ListView(LoginRequiredMixin, ExportMixin, SingleTableView):
...
def get_context_data(self, **kwargs):
context = super(ListView, self).get_context_data(**kwargs)
table = self.get_table(**self.get_table_kwargs())
table.request = self.request # ADDING REQUEST DIRECTLY TO TABLE
context[self.context_filter_name] = self.filter
context['firstname'] = str(self.request.user.first_name)
return context
def get_table(self, **kwargs):
table_class = self.get_table_class()
table = table_class(data=self.get_table_data(), **kwargs)
return RequestConfig(self.request, paginate={'per_page':self.paginate_by}).configure(
table
)
tables.py
class Table(tables.Table):
...
def get_top_pinned_data(self):
id_list = MODEL.objects.filter(USERNAME=self.request.user.username).values_list('id', flat=True)
pinned = MODEL.objects.filter(id__in=id_list)
return pinned
Can anyone please help me out?
**EDIT: adding request to table in get_table function
views.py
class ListView(LoginRequiredMixin, ExportMixin, SingleTableView):
...
def get_context_data(self, **kwargs):
context = super(ListView, self).get_context_data(**kwargs)
context[self.context_filter_name] = self.filter
context['firstname'] = str(self.request.user.first_name)
return context
def get_table(self, **kwargs):
table_class = self.get_table_class()
table = table_class(data=self.get_table_data(), **kwargs)
table.request = self.request
return RequestConfig(self.request, paginate={'per_page':self.paginate_by}).configure(
table
)

I find it cleaner to put table.request inside of get_table instead,
def get_table(self, **kwargs):
"""
Return a table object to use. The table has automatic support for
sorting and pagination.
"""
table_class = self.get_table_class()
table = table_class(data=self.get_table_data(), **kwargs)
table.request = self.request
return RequestConfig(
self.request, paginate=self.get_table_pagination(table)
).configure(table)
If you still experience AttributeError, request is used before its set in get_table, a potential solution would be the following:
def get_table(self, **kwargs):
"""
Return a table object to use. The table has automatic support for
sorting and pagination.
"""
table_class = self.get_table_class()
table = table_class(data=self.get_table_data(), request=self.request, **kwargs)
table.request = self.request
return RequestConfig(
self.request, paginate=self.get_table_pagination(table)
).configure(table)
And get the request object from your Tables init method like:
class Table(tables.Table):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request")
super().__init__(*args, **kwargs)
def get_top_pinned_data(self):
id_list = MODEL.objects.filter(USERNAME=self.request.user.username).values_list('id', flat=True)
pinned = MODEL.objects.filter(id__in=id_list)
return pinned

Related

How to use the same ModelForm form different admin classes with different models

I'm trying to create a common form with a date range validation that will be used in 20 admin classes with different model
So I'm creating this mixin for each of them to use
class DateControllerMixin():
def get_queryset(self, request):
qs = dateControlQuerySet(super().get_queryset(request), self.parameter_name, request)
return qs
def get_form(self, request, obj=None, change= None,**kwargs):
print(self.parameter_name)
if request.user.groups.filter(name='slaughterhouse_date_controlled').exists():
form = DateControlledForm
form.model_class = self.model
form.parameter_name = self.parameter_name
return form
return super().get_form(request, obj, change, **kwargs)
And this is the form but I can't find a way to make the form without specifying the model class in the Meta or use class attributes
class DateControlledForm(forms.ModelForm):
def __init__(
self, *args, **kwargs
):
self._meta.model = self.model_class
super(DateControlledForm, self).__init__(*args, **kwargs)
class Meta:
# model = self.model_class
fields='__all__'
widgets = {
'the_date': AdminDateWidget(),
}
def clean(self, **kwargs):
print(self.paramter_name)
date = self.cleaned_data.get('the_date')
today = datetime.today().date()
days_limit = AppConfigurations.objects.get(parameter_name=self.parameter_name).parameter_value
first_day = today - timedelta(days = int(days_limit))
if date < first_day:
raise forms.ValidationError({
'the_date': [_(f"Date cannot be before {first_day}.")]})
return self.cleaned_data
I tried to edit the meta options from the init method but it didn't work

Combine DetailView and SingleTableView in Django

How can I display a SingleTableView table together with a DetailView in django? What I want is to include or combine both into one. So that when calling DeviceDetailView i receive the device's details and a table of the devices's data (stored also in another model).
I am using django-tables2 for the table.
Someone any idea?
Thank you
class DataListView(SingleTableView):
model = Data
template_name = 'data/list.html'
table_class = DataTable
def post(self, request, *args, **kwargs):
queryset = request.POST
for i in queryset['list'].split(","):
if queryset['action'] == 'delete':
Data.objects.filter(data_id=i).delete()
return HttpResponseRedirect('/data/')
class DeviceDetailView(DetailView):
template_name = 'devices/detail.html'
def get_object(self):
id_ = self.kwargs.get("id")
return get_object_or_404(Device, id=id_)
EDIT
I know it should look something like this:
class DeviceDetailView(SingleTableView, DetailView):
template_name = "devices/device-detail.html"
model = Device
table_class = DeviceTable
def get_context_data(self, **kwargs):
self.object = self.get_object()
context = super(DetailView, self).get_context_data(**kwargs)
id_ = self.kwargs.get("id")
obj = Device.objects.filter(id=id_)
context['object'] = obj
return context
But i think i still don't know how to do this part inside get_context_data so that the elements are merged together...
SOLUTION
Ok it is donde like this:
class DeviceDetailView(SingleTableView, DetailView):
template_name = 'devices/device-detail.html'
model = Data
table_class = DataTable
#crumbs = [('Device detail', 'DeviceDetailView')]
def get_context_data(self, **kwargs):
self.object = self.get_object()
context = super(DetailView, self).get_context_data(**kwargs)
table = self.get_table(**self.get_table_kwargs())
context[self.get_context_table_name(table)] = table
return context
def get_object(self):
id_ = self.kwargs.get("id")
return get_object_or_404(Device, id=id_)
You can serve the table within your DetailsView using custom get_context_data. You can render your table in the template as you usually do with {% render_table table %}.
# import YourModel
# import YourTable
class DeviceDetailView(DetailView):
model = YourModel
template_name = 'devices/detail.html'
def get_context_data(self, **kwargs):
table = YourTable(YourModel.objects.all()) # define your tables data here
context = {"table": table}
return super().get_context_data(**context)

Django DetailView and get request

I have a detail view with a drop-down list. The user can choose an item in the drop-down list and the information about that item should appear below it. This requires that the DetailView includes something like this:
def get_context_data(self, **kwargs):
context = super(InvoiceDetail, self).dispatch(*args, **kwargs)
request = self.request
if request.GET:
try:
invoice_selector = request.GET.get('invoice_selector', None)
invoice = Invoice.objects.get(id = int(invoice_selector) ) # either a string representing a number or 'add invoice'
context_object_model = invoice
except ValueError:
return HttpResponseRedirect(reverse('accounting:add_invoice'))
return context
How do I over-write the context_object_model? The above code does not make the change.
This is not something you should do in get_context_data. You should check for "add invoice" in the get method, and do the rest in get_object.
class MyDetailView(DetailView):
...
def get(self, request, *args, **kwargs):
self.invoice_selector = request.GET.get('invoice_selector')
if self.invoice_selector = 'add invoice':
return HttpResponseRedirect(reverse('accounting:add_invoice'))
return super().get(request, *args, **kwargs)
def get_object(self):
if self.invoice_selector:
obj = self.model.objects.get(pk=self.invoice_selector)
else:
obj = super().get_object()
return obj

Django - Common data, few views

I need your help because I have approximately 6 views having common data.
Example similar to my problem :
def informationPlayers(request,nameTeam):
try :
team = Team.objects.get(name = nameTeam)
except Team.DoesNotExist:
return redirect(teams)
if not request.user.is_authenticated():
formLogin = loginForm(auto_id=False)
countMembers = team.members.count()
else :
members = team.members.members()
... code specific to this view ...
def informationCoach(request,nameTeam):
try :
team = Team.objects.get(name = nameTeam)
except Team.DoesNotExist:
return redirect(teams)
if not request.user.is_authenticated():
formLogin = loginForm(auto_id=False)
countMembers = team.members.count()
else :
members = team.members.members()
... code specific to this view ...
So these 2 views have same variables and an algorithm (if user is authenticated or not).
I don't want to write this algortihm and variables in all views using them, How can I do please ?
I already try TEMPLATE_CONTEXT_PROCESSORS but it's applied in all views/pages of my site, I don't want that.
Here is an answer using class base views. Classes in Python (and any other language that supports them) is a good way to share code while making it obvious to any other developer which views should and should not share this code.
Your urls.py should be kept about the same, except that you will need to call the class methods. Something like:
from django.conf.urls import *
from .views import *
urlpatterns = [
url(r'^(?P<nameTeam>[^/]+)/player/$', PlayerView.as_view(), name='player_view'),
url(r'^(?P<nameTeam>[^/]+)/coach/$', CoachView.as_view(), name='player_view'),
]
and then on your views.py, you would do something like:
class TeamAccessMixin(object):
# This is really just a regular Python object
team = None
formLogin = None
countMembers = None
members = None
def get_team(self, teamName):
try :
self.team = Team.objects.get(name = nameTeam)
except Team.DoesNotExist:
return redirect(self.teams)
if not self.request.user.is_authenticated():
self.formLogin = loginForm(auto_id=False)
self.countMembers = team.members.count()
else :
self.members = team.members.members()
class PlayerView(TeamAccessMixin, DetailView):
model = Player
template_name = 'team/player.html'
def get_context_data(self, **kwargs):
context = super(PlayerView, self).get_context_data(**kwargs)
self.get_team(kwargs['teamName']);
context['team'] = self.team
context['members'] = self.members
# Special code to add additional context specific to this view
...
return context
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
return super(PlayerView, self).dispatch(request, *args, **kwargs)
class CoachView(TeamAccessMixin, DetailView):
model = Coach
template_name = 'team/coach.html'
def get_context_data(self, **kwargs):
context = super(CoachView, self).get_context_data(**kwargs)
self.get_team(kwargs['teamName']);
context['team'] = self.team
context['members'] = self.members
# Special code to add additional context specific to this view
...
return context
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
return super(CoachView, self).dispatch(request, *args, **kwargs)
Just answering your specific question, but I also see you may not understand other aspects of Django (like login decorators, which will ensure you users are logged in or will automatically redirect them). So, as an added bonus, here is how I would have implemented what you are trying to do (assuming some details you are not specifying):
class TeamAccessMixin(object):
# This is really just a regular Python object
def get_object(self, queryset=None):
# It is best practice to simply give a 404 if the record does not exist
object = get_object_or_404(Team, slug=self.kwargs['nameTeam'])
return object
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
# the decorator will ensure all users are logged in or will redirect to login page
return super(TeamAccessMixin, self).dispatch(request, *args, **kwargs)
class PlayerView(TeamAccessMixin, DetailView):
model = Player
template_name = 'team/player.html'
def get_context_data(self, **kwargs):
context = super(PlayerView, self).get_context_data(**kwargs)
context['team'] = self.object
context['members'] = self.object.members
# Special code to add additional context specific to this view
...
return context
class CoachView(TeamAccessMixin, DetailView):
model = Player
template_name = 'team/coach.html'
def get_context_data(self, **kwargs):
context = super(CoachView, self).get_context_data(**kwargs)
context['team'] = self.object
context['members'] = self.object.members
# Special code to add additional context specific to this view
...
return context
Thank you #dkarchmer, I did something like that :
version modified :
class ProjetMixin(object) :
project = None
coms = None
form = None
def get_context_data(self, *args, **kwargs) :
context = super(ProjetMixin, self).get_context_data(*args, **kwargs)
try :
self.project = Project.objects.get(name = self.kwargs['name']) #in urls
except Project.DoesNotExist:
return redirect(projects)
self.coms = self.project.coms_set.order_by('-date_send')
if not self.request.user.is_authenticated():
self.form = formForm(auto_id=False)
context['project'] = self.project
context['coms'] = self.coms
context['form'] = self.form
return context
class FicheProjetView(ProjetMixin, TemplateView):
template_name = 'path/homeProject.html'
def get_context_data(self, **kwargs):
context = super(FicheProjetView, self).get_context_data(**kwargs)
if self.request.POST :
...
return context
def dispatch(self, request, *args, **kwargs):
return super(FicheProjetView, self).dispatch(request, *args, **kwargs)
It's proper ?

django object is not iterable with custom instance

I am trying to leave my object itself out of the queryset of possible options. Problem is i get the error: 'Country' object is not iterable
Not sure where i am going wrong.
My view:
def edit_country(request, country_id):
country = get_object_or_404(Country, pk=country_id)
country_form = CountryForm(instance=country)
return render(request, 'create_country.html', {'country_form': country_form})
My form init:
def __init__(self, *args, **kwargs):
super(CountryForm, self).__init__(*args, **kwargs)
if 'instance' in kwargs:
self.fields['likes'].queryset = Country.objects.exclude(kwargs['instance'])
self.fields['hates'].queryset = Country.objects.exclude(kwargs['instance'])
Where do i go wrong?
Change the order of the method, so you pop the kwarg first. You are sending the kwarg to super.
def __init__(self, *args, **kwargs):
instance = kwargs.pop('instance', None)
#all other stuff