I am trying to implement an MVC like pattern in my views.
Basically I have a base View which serves a normal type, and type1 view and type2 view that extends the base view.
I also have a controller and a model to get and set data.
I need to pass this model over to the view.
But I always get a KeyError when I access the kwargs dict from the view.
I am new to django and python. So please don't mind if I am missing something obvious.
Here is the code
class Controller():
NORMAL, TYPE1 , TYPE2 = (0,1,2)
#classmethod
def controller_init(cls): #entry point to view. Called in urls.py
def view(request,slug,*args,**kwargs):
self = cls()
self.request = request
self.slug = slug
self.args = args
self.kwargs = kwargs
return self.start()
return view
def start(self):
modal = Modal()
self.kwargs['modal'] = modal
modal.init(self.slug)
ptype = modal.getPtype()
return self.showView(ptype)
def showView(self,viewType):
if(viewType == self.NORMAL):
view_handler = View1.as_view()
elif(projectType == self.TYPE1):
view_handler = ExtendedView1.as_view()
else:
view_handler = ExtendedView2.as_view()
return view_handler(self.request,self.args,self.kwargs)
Here is my view :
from django.views.generic.base import TemplateView
class View1(TemplateView):
template_name = "view_detail.html"
def get_context_data(self, **kwargs):
context = super(View1, self).get_context_data(**kwargs)
self.modal = kwargs['modal']
context['pid'] = self.modal.getPID()
context['title'] = "This is a normal"
return context
When I run the page, I get a KeyError saying the key modal is not defined.
Related
I have 2 linked models for thesaurus with labels in English and French
I have a 2 class methods (options_list_eng and options_list_fra) that return a list of labels based on code pass in parameters: one that returns french labels and the other return English labels
I also have a request.session['language'] that catch the browser language of the user
I want to use theses methods to set choices attributes in my form depending on the language (value in request.session.get('language'))
I try use something like that but it do not works
if self.language == 'en':
TYPES = Thesaurus.options_list_eng(1)
else:
TYPES = Thesaurus.options_list_fra(1)
below my current code that runs well but does not allow french/english translation of choices
forms.py
class RandomisationForm(forms.ModelForm):
def __init__(self, request, *args, **kwargs):
self.request = request
self.language = request.session.get('language')
super(RandomisationForm, self).__init__(*args, **kwargs)
TYPES = Thesaurus.options_list_eng(1)
ran_pro = forms.ChoiceField(label = _("Type"), widget=forms.Select, choices=TYPES)
You can define field in form's __init__ method like this:
class RandomisationForm(forms.ModelForm):
def __init__(self, request, *args, **kwargs):
super(RandomisationForm, self).__init__(*args, **kwargs)
self.request = request
self.language = request.session.get('language')
if self.language == 'en':
TYPES = Thesaurus.options_list_eng(1)
else:
TYPES = Thesaurus.options_list_fra(1)
self.fields["ran_pro"] = forms.ChoiceField(label = _("Type"), widget=forms.Select, choices=TYPES)
Don't see any SuccessMessage. Anyone can tell me why I don't get any success message once I successfully created the database entry?
class TicketCreate(AdminPermissionRequiredMixin, SuccessMessageMixin, FormValidationMixin, BaseTicketView, TemplateView):
template_name = 'tickets/admin/create.html'
success_message = _("Ticket has been successfully created.")
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['ticket_form'] = self.ticket_form
context['tax_form'] = self.tax_form
return context
#cached_property
def ticket_form(self):
return TicketForm(data=self.request.POST or None, event=self.request.event)
#cached_property
def tax_form(self):
return TicketTaxForm(prefix='tax', data=self.request.POST or None)
#transaction.atomic
def post(self, request, *args, **kwargs):
if self.ticket_form.is_valid() and self.tax_form.is_valid():
tax_instance = self.tax_form.save(commit=False)
tax_choice = self.tax_form.cleaned_data.get('tax_choice')
new_tax = (tax_choice == TicketTaxChoice.NEW_TAX and
tax_instance.name and tax_instance.percentage)
# save the tax instance
if new_tax:
tax_instance.event = self.request.event
tax_instance.save()
# save the ticket instance
ticket_instance = self.ticket_form.save(commit=False)
ticket_instance.event = self.request.event
if new_tax:
ticket_instance.tax = tax_instance
self.ticket_form.save()
return redirect(
'tickets:admin:detail',
self.request.organizer.slug,
self.request.event.slug,
ticket_instance.pk
)
return super().get(request, *args, **kwargs)
From the official docs:
Adds a success message attribute to FormView based classes
I can't see your other classes, but from what I see your class is based on TemplateView, which is not derived from FormView.
Looking the source you can see that message is shown when form_valid is called, so your class should at least specify form_class or model field. But you are calling is_valid() manually, so form_valid is not called and message is not shown.
Im trying to create a form that will show a list of checkboxes based on a models items. Then also to be able to filter this list if required.
However I am getting the below error and am not sure as to why?
error:
File "/usr/local/lib/python3.6/site-packages/django/forms/forms.py" in errors
174. if self._errors is None:
Exception Type: AttributeError at /sites/site/auto_gen_subnets/7
Exception Value: 'AutoSubnetForm' object has no attribute '_errors'
forms.py
class AutoSubnetForm(forms.Form):
subnet_type_data = SiteTypes.objects.all()
def __init__(self, *args, **kwargs):
self.site_type = kwargs.pop("site_type")
# get site type if set and filter against it
if self.site_type:
subnet_type_data = SiteTypes.objects.filter(site_type=self.site_type)
# create list for types
subnet_types = []
for stype in subnet_type_data:
# add tuple for each type
subnet_types.append((stype.id,stype.site_type))
subnets = forms.ChoiceField(
choices=subnet_types,
widget = forms.Select(
attrs = {'class': 'form-control'}
)
)
views.py:
#login_required
#user_passes_test(lambda u: u.has_perm('config.add_subnet'))
def auto_gen_subnets(request, site_id):
#generate_subnets(site_id)
from config.models import SubnetTypes
site_data = get_object_or_404(SiteData.objects.select_related('site_type'),pk=site_id)
subnets = None
if request.method == 'GET':
form = AutoSubnetForm(site_type=site_data.site_type)
else:
# A POST request: Handle Form Upload
form = AutoSubnetForm(request.POST)
# If data is valid, proceeds to create a new post and redirect the user
if form.is_valid():
subnets = form.cleaned_data['subnets']
return render(request, 'sites/generate_subnets.html', {
'data': subnets,
'subnet_form': form,
'SiteName' : site_data.location,
'SiteID' : site_id,
}
)
You override the init method. So you should return it to your superclass.
def __init__(self, *args, **kwargs):
self.site_type = kwargs.pop("site_type")
# get site type if set and filter against it
if self.site_type:
subnet_type_data = SiteTypes.objects.filter(site_type=self.site_type)
super(AutoSubnetForm, self).__init__(*args, **kwargs)
You do not return your _errors. So it does not know about any other data except the ones you provide while overriding. If you want all you should return it to superclass. That should cause it. The code above should fix it.
I wanto to redirect to an url if the object of GroupMember doesn't exist but shows this error:
TypeError: context must be a dict rather than str.
Here is my view:
class GroupDetail(DetailView):
template_name = "group_detail.html"
model = Group
def get_context_data(self, **kwargs):
context = super(GroupDetail, self).get_context_data(**kwargs)
# Code
try:
group_member = GroupMember.objects.get(member=volunteer, group=group)
context['group_member'] = group_member
# Code
return context
except:
return reverse('users:home')
I try with redirect and reverse_lazy but shows the same error and I tried with
reverse('users:home', {}), reverse('users:home', kwargs={})
and
reverse('users:home', kwargs=None)
First, this always goes wrong, because there is no member or volunteer in the local data.
Secondly, the proper way to do this is to return None or an empty dict and override render_to_response:
from django.views.generic import DetailView
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.contrib.messages import warning
class GroupDetail(DetailView):
def get_context_data(self, **kwargs):
volunteer = self.get_volunteer() # Or something like that
group = self.get_group() # Or something like that
try:
group_member = GroupMember.objects.get(
member=volunteer, group=group
)
return super(GroupDetail, self).get_context_data(
group_member=group_member, **kwargs
)
except GroupMember.DoesNotExist:
return None
# All other exceptions should really be raised as they are
# actual application errors.
def render_to_response(self, context, **response_kwargs):
if context is None:
warning(self.request, 'You are groupless! Peer pressure incoming.')
return HttpResponseRedirect(reverse("users:home"))
return super(GroupDetail, self).render_to_response(
context, **response_kwargs
)
This way, you make full use of the API and can extend and override the bit you need, which is why Class Based Views were designed.
If your code throws an exception it will run return reverse('users:home') which yields str data type.
However django docs states clearly:
get_context_data(**kwargs)
Returns a dictionary representing the template context.
You need to do something like that:
def get_context_data(self, **kwargs):
context = super(GroupDetail, self).get_context_data(**kwargs)
# Code
try:
group_member = GroupMember.objects.get(member=volunteer,group=group)
context['group_member'] = group_member
# Code
finally:
return context
or you could do this if you want to redirect:
def get_context_data(self, **kwargs):
context = super(GroupDetail, self).get_context_data(**kwargs)
# Code
try:
group_member = GroupMember.objects.get(member=volunteer,group=group)
context['group_member'] = group_member
# Code
except:
return HttpResponseRedirect(reverse('users:home'))
The get_context_data() method must return a dictionary representing the template context, not a URL string. To redirect on an exception you could override the get() method instead:
from django.http HttpResponseRedirect
class GroupDetail(DetailView):
template_name = "group_detail.html"
model = Group
def get(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data(object=self.object)
try:
# your code
return self.render_to_response(context)
except:
return HttpResponseRedirect(reverse("users:home"))
You can check the source code of DetailView and its ancestors here.
How in Django i can access requset in form?
I need this to get data tuple to pass in choices to form.
Below init approach doesn't work: NameError: name 'request' is not defined, with self or without: self.request.GET.get('project') or request.GET.get('project')
class PostfilterForm(forms.Form):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request")
super(PostfilterForm, self).__init__(*args, **kwargs)
monitoring_words_to_show = Nlpmonitorword.objects.filter(monitoringwords__name = self.request.GET.get('project'))
words_list = []
for word in monitoring_words_to_show:
words_list.append((word.monitor_word, word.monitor_word))
words_list = tuple(words_list) # trying to get here tuple to pass in choises (('vk', 'vk'), ('fb', 'fb'), ('vkfb', 'vkfb'))
project = forms.CharField(required=True, label='')
monitor = forms.MultipleChoiceField(widget=forms.SelectMultiple, choices=words_list, required=False, label='')
All the code you're trying to use isn't used within a method which means it doesn't belong to any instance of a PostFilterForm and therefore has no knowledge of self let alone its fields.
You should include these in a function, although what function that should be is unclear.
def my_function(self):
monitoring_words_to_show = Nlpmonitorword.objects.filter(monitoringwords__name = self.request.GET.get('project'))
words_list = []
for word in monitoring_words_to_show:
words_list.append((word.monitor_word, word.monitor_word))
words_list = tuple(words_list) # trying to get here tuple to pass in choises (('vk', 'vk'), ('fb', 'fb'), ('vkfb', 'vkfb'))
What your form needs is not the request it's the project. It's better to deal with the request in the view and pass the required parameters to the form:
Form:
class PostfilterForm(forms.Form):
def __init__(self, project, *args, **kwargs):
self.project = project
View:
project = request.GET.get('project')
form = PostfilterForm(project, request.POST)