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
Related
I'm rewriting my function to class based views, this is the function I currently have.
#login_required
def invoice(request, invoice_no, template_name="invoice.html"):
context = {}
invoice_exists = Invoice.objects.filter(invoice_no=invoice_no)
if invoice_exists:
context['invoice'] = invoice_exists.first()
else:
return HttpResponseRedirect(reverse('invoices'))
return render(request, template_name, context)
you have to be logged in, it filters using a filter named invoice_no
path('invoice/<int:invoice_no>', views.InvoiceView.as_view(), name="invoice"),
and if a match is found returns it, if not redirects you back to the invoices page.
this is what I have as a class
class InvoiceView(DetailView):
queryset = Invoice.objects.all()
context_object_name = 'invoice'
pk_url_kwarg = 'invoice_no'
template_name = "invoice.html"
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)
def get_object(self):
obj = super().get_object()
return obj
also the get object or 404 will do also since all it needs is a 404 page and it'll work.
Try this:
class ArticleDetailView(LoginRequiredMixin, DetailView):
template_name = "invoice.html"
context_object_name = 'invoice'
model = Invoice
def dispatch(self, request, *args, **kwargs):
try:
return super().dispatch(request, *args, **kwargs)
except Invoice.DoesNotExist:
return HttpResponseRedirect(reverse('invoices'))
def get_object(self):
return Invoice.objects.get(invoice_no=self.kwargs['invoice_no'])
Adjust according to your code.
from django.contrib.auth.mixins import LoginRequiredMixin
class InvoiceView(LoginRequiredMixin, DetailView):
template_name = "invoice.html"
context_object_name = 'invoice'
def get_queryset(self, *args, **kwargs):
invoice = get_object_or_404(Invoice, invoice_no=kwargs['invoice_no'])
return invoice
This will however return a 404 page if no data is found, if you like it to redirect it to invoices page, use a filter. Then use an IF statement to compare length > 0, if 0 then just redirect to page. Might as well put a message error then too.
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
I have a form for which I'd like to use user data to filter the content of a choicefield.
Following this solution, I added all references to user in the __init__ function of my Form class:
class MyChoiceField(forms.Form):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user')
super(MyChoiceField, self).__init__(*args, **kwargs)
user_id = self.user.id
myobjects = forms.ModelChoiceField(label='',queryset = Myobject.objects.values_list('name', flat=True).exclude(name__isnull=True).filter(Q(person__isnull=True) | Q(person=user_id)).distinct(),empty_label=None)
And in the view I call it as:
def my_view(request):
my_list = MyChoiceField(user = request.user)
context = {
'my_list': my_list,
}
return render(request, 'foo/bar.html', context)
Debugging the __init__ part indicates the queryset content is correct, but in the view, my_list contains the following:<MyChoiceField bound=False, valid=Unknown, fields=()>.
Should I include something in the form class, outside of the __init__ part for this to work?
try this
class MyChoiceField(forms.Form):
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(MyChoiceField, self).__init__(*args, **kwargs)
user_id = user.id
Currently Happening:
Dynamically generated form and form fields are being displayed.
Enter some data into the said fields, but self.get_all_cleaned_data() returns nothing.
Form returns to page 0 instead of submitting the form and using done()
What I want to happen:
- Data in fields to be retained and displayed when going back, or to the confirmation page
- Form to actually submit and use done() to process and save
The following the my forms.py
class OrderForm(forms.Form):
class Meta:
localized_fields = ('__all__',)
def __init__(self, *args, **kwargs):
self.fields = kwargs.pop('fields')
fields = self.fields
super(OrderForm, self).__init__(*args, **kwargs)
if not isinstance(fields, str):
for i in fields.fields.all():
widget = forms.TextInput()
_type = forms.CharField
if i.field_type == Field.TEXTAREA_FIELD:
widget = forms.Textarea
...
self.fields[i.name] = _type(**fields)
This is supposed to get Database created forms and field data and generate fields accordingly. For example:
Form A has fields:
Name (Regular Text Field)
Address (Textarea)
The above code will then generate fields for those.
The next block of code is from my views.py file
FORM_TEMPLATES = {
"0": 'order/details.html',
"1": 'order/details.html',
"2": 'order/details.html',
"3": 'order/details.html',
"4": 'order/details.html',
"confirm": 'order/confirm.html',
}
class Order(SessionWizardView):
form_list = [OrderForm]
def get_current_step_form(self, company, *args, **kwargs):
step_form = [Form.objects.all()]
step_form.append('Confirm')
return step_form
def get_context_data(self, form, **kwargs):
context = super(Order, self).get_context_data(form=form, **kwargs)
# Returns {}, but I want this to return all previous field values
context.update({
'all_data': self.get_all_cleaned_data(),
})
return context
def post(self, *args, **kwargs):
go_to_step = self.request.POST.get('wizard_goto_step', None)
form = self.get_form(data=self.request.POST)
current_index = self.get_step_index(self.steps.current)
goto_index = self.get_step_index(go_to_step)
if current_index > goto_index:
self.storage.set_step_data(self.steps.current,
self.process_step(form))
self.storage.set_step_files(self.steps.current,
self.process_step_files(form))
return super(Order, self).post(*args, **kwargs)
def get_form(self, step=None, data=None, files=None):
"""
Get the form and add to form_list
"""
form = super(Order, self).get_form(step, data, files)
company = ...
get_forms = self.get_current_step_form(company=company)
form_list_value = dict(self.form_list)['0']
while len(self.form_list.items()) < len(get_forms):
self.form_list.update({str(len(self.form_list.items())): form_list_value})
return form
def done(self, form_list, **kwargs):
return HttpResponse("View")
done() is a work in progress, but it doesn't even seem to reach that point, as it keeps going from (for example) Form 0-1-2-3-0-...
The confirm form will not have any field values form the previous pages and will only return {}
Any help would be appreciated,
Thanks
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 ?