Overriding save() model method for permissions - django

Am trying to override save() method so that only the creator and administrator are able to update the model field values as :
if not self.pk:
super(Shastra, self).save(*args, **kwargs)
else:
if (self.person == args[0].user) or (self.person.is_superuser):
super(Shastra, self).save(*args, **kwargs)
While update am passing request to the save method as
def edit(request, shastra_id):
shastra_id = int(shastra_id)
shastra = Shastra.objects.get(pk = shastra_id )
if request.method == 'POST':
form_shastra_edit = ShastraEditForm(request.POST, instance = shastra)
if form_shastra_edit.is_valid():
form_shastra_edit.save(request)
return HttpResponseRedirect('/edited/successfully')
else:
form_shastra_edit = ShastraEditForm(instance = shastra)
But am getting " tuple index out of range " error . What is going wrong in this ?

You can not use request in a Model.save() method. You have to do request-based validation in your views method (in your edit method for instance). The Model layer is unaware of 'request' objects..
Make your edit method something like:
def edit(request, shastra_id):
shastra_id = int(shastra_id)
shastra = Shastra.objects.get(pk = shastra_id )
if request.method == 'POST':
form_shastra_edit = ShastraEditForm(request.POST, instance = shastra)
if form_shastra_edit.is_valid() and shastra.user == request.user:
form_shastra_edit.save(request)
return HttpResponseRedirect('/edited/successfully')
else:
form_shastra_edit = ShastraEditForm(instance = shastra)

Related

Django form class and view class connected

Hi in my code(not written by me) i have django form class and views class. I dont know how this is connected each other. Can anyone tell me how this is connected? Also can any one please tell me how this messege : Credential is in use by {0} collections that are turned on and "
"{1} collections that are turned off. Be mindful that over-using " "credentials may result in collecting being rate limited by the " "social media API is displayed, i mean if i need to change the alignment of this text where i should change?
My code classes are :
from forms.py :
class CollectionTwitterSearch2Form(BaseCollectionForm):
incremental = forms.BooleanField(initial=True, required=False, label=INCREMENTAL_LABEL, help_text=INCREMENTAL_HELP)
def __init__(self, *args, **kwargs):
super(CollectionTwitterSearch2Form, self).__init__(*args, **kwargs)
self.helper.layout[0][5].extend(('incremental',))
if self.instance and self.instance.harvest_options:
harvest_options = json.loads(self.instance.harvest_options)
if "incremental" in harvest_options:
self.fields['incremental'].initial = harvest_options["incremental"]
def save(self, commit=True):
m = super(CollectionTwitterSearch2Form, self).save(commit=False)
m.harvest_type = Collection.TWITTER_SEARCH_2
harvest_options = {
"incremental": self.cleaned_data["incremental"],
}
m.harvest_options = json.dumps(harvest_options, sort_keys=True)
m.save()
return m
from views.py :
def _get_credential_use_map(credentials, harvest_type):
credential_use_map = {}
if harvest_type in Collection.RATE_LIMITED_HARVEST_TYPES:
for credential in credentials:
active_collections = 0
inactive_collections = 0
for collection in credential.collections.all():
if collection.is_on:
active_collections += 1
else:
inactive_collections += 1
if active_collections == 0 and inactive_collections == 0:
credential_use_map[credential.id] = ("", "")
else:
credential_use_map[credential.id] = ("warning",
"Credential is in use by {0} collections that are turned on and "
"{1} collections that are turned off. Be mindful that over-using "
"credentials may result in collecting being rate limited by the "
"social media API.".format(active_collections,
inactive_collections))
return credential_use_map
class CollectionCreateView(LoginRequiredMixin, CollectionSetOrSuperuserPermissionMixin, SuccessMessageMixin,
CreateView):
model = Collection
template_name = 'ui/collection_create.html'
def get_initial(self):
initial = super(CollectionCreateView, self).get_initial()
initial["collection_set"] = CollectionSet.objects.get(pk=self.kwargs["collection_set_pk"])
return initial
def get_context_data(self, **kwargs):
context = super(CollectionCreateView, self).get_context_data(**kwargs)
context["collection_set"] = CollectionSet.objects.get(pk=self.kwargs["collection_set_pk"])
harvest_type = self.kwargs["harvest_type"]
context["harvest_type_name"] = _get_harvest_type_name(harvest_type)
credentials = _get_credential_list(self.kwargs["collection_set_pk"], harvest_type)
context["credentials"] = credentials
context["credential_use_map"] = _get_credential_use_map(credentials, harvest_type)
context["platform"] = Collection.HARVEST_TYPES_TO_PLATFORM[self.kwargs["harvest_type"]]
return context
def get_form_kwargs(self):
kwargs = super(CollectionCreateView, self).get_form_kwargs()
kwargs["coll"] = self.kwargs["collection_set_pk"]
kwargs['credential_list'] = _get_credential_list(self.kwargs["collection_set_pk"], self.kwargs["harvest_type"])
return kwargs
def get_form_class(self):
return getattr(forms, _get_collection_form_class(self.kwargs["harvest_type"]))
def get_success_url(self):
return reverse('collection_detail', args=(self.object.pk,))
def get_success_message(self, cleaned_data):
if self.object.required_seed_count() != 0:
return "New collection added. You can now add seeds."
return "New collection added."
Full code is here in this git : https://github.com/gwu-libraries/sfm-ui/tree/master/sfm/ui
It would be great anyone can explain how these two classes and template is connected and how the messege is displayed
The CollectionCreateView class is conected to the Form using the function get_form_class, this function is called by default by the CreateView, in there you can see is calling _get_collection_form_class() and as an argument is passing self.kwargs['harvest_type'] this kwargs is comming from the url declaration. The _get_collection_form_class function is returning the CollectionTwitterSearch2Form when the harvest_type is something like TwitterSearch2. The template is given by the template_name = 'ui/collection_create.html' again this is the default vehaviour. And finally for the message this is using SuccessMessageMixin.

How to render form with errors in generic detail view

I am trying to render a generic detail view page with a form with errors
My post method in my generic detail view is
def post(self, request, slug):
if 'submit-edit-roster' in request.POST:
edit_roster_form = EditRosterForm(request.POST, team=self.request.user.playerprofile.team)
if edit_roster_form.is_valid():
edit_roster_form.save()
return redirect ('tcl-team', slug=self.request.user.playerprofile.team.urlslug)
my edit roster form is
class EditRosterForm(forms.Form):
members = 0
team = None
sublist = []
playerlist = []
def __init__(self, *args, **kwargs):
self.team = kwargs.pop('team', None)
self.members = 0
self.sublist = []
self.playerlist = []
super(EditRosterForm, self).__init__(*args, **kwargs)
currentroster = Roster.objects.filter(team=self.team)[0]
for member in Playerprofile.objects.filter(team=self.team).order_by('name'):
if member in currentroster.players.all():
self.fields[str(member.name)] = forms.ChoiceField(choices=ROSTER_CHOICES)
self.initial[str(member.name)] = '1'
elif member in currentroster.subs.all():
self.fields[str(member.name)] = forms.ChoiceField(choices=ROSTER_CHOICES)
self.initial[str(member.name)] = '2'
self.members += 1
def clean(self):
cleaned_data = super().clean()
i = 0
for member in Playerprofile.objects.filter(team=self.team):
if cleaned_data[member.name] == '1':
self.playerlist.append(member)
elif cleaned_data[member.name] == '2':
self.sublist.append(member)
i += 1
print(len(self.sublist))
if len(self.sublist) > 2:
raise ValidationError("Maximum of 2 subs allowed")
if len(self.playerlist) > 5:
raise ValidationError("Maximum of 5 players allowed")
if len(self.playerlist) + len(self.sublist) > i:
raise ValidationError("Team members must be a sub or a player")
return cleaned_data
def save(self):
cleaned_data = self.cleaned_data
print(cleaned_data)
UpdateRoster(roster=self.team.GetCurrentRoster(), players=self.playerlist, subs=self.sublist)
When my form has errors I get
The view team.views.TeamEditView didn't return an HttpResponse object. It returned None instead.
I know I need to add something to pass my form the form object with errors but can't find how to do this.
if edit_roster_form.is_valid():
edit_roster_form.save()
return redirect ('tcl-team', slug=self.request.user.playerprofile.team.urlslug)
else:
# Render detail view page with form
Apologies if my question is incoherent, I'm new to django and stackoverflow
I think you have to add request as parameter to the render shortcut

Render django.forms.fields.ChoiceField object

In my Django Project I have the following Problem:
I would like to have a dynamic Django form. In the first step the user is asked something by the first form. When I get the postmethod the variables should be used for genereating a new form
my views.py
def calc(request):
if request.method =="POST":
get_form = CalculationForm(request.POST)
if get_form.is_valid():
op = get_form.cleaned_data['op']
ab = get_form.cleaned_data['ab']
alternative = AlternativForm(optype = op, wsgroup = ab)
return render(request, 'calculated_lensar.html', {"alternativ" : alternativ})
else:
form = CalculationForm()
return render(request, 'calc.html', {'form': form})
The secondform (postmethod) looks like
class AlternativForm(forms.Form):
praep_button = ((3, 'hallo'), (4, 'tschüss'))
def __init__(self, optype, wsgroup, *args, **kwargs):
super(AlternativForm, self).__init__(*args, **kwargs) #dont know for what this is standing
self.optype = optype
self.wsgroup = wsgroup
self.values = self.read_db()
self.praep_button = self.buttons()
self.felder = self.blub()
self.neu2 = self.myfield_choices()
def read_db(self):
import sqlite3
....
return result #tuple with 15x5 elements
def buttons(self):
praep_button = []
for i in self.values:
praep_button.append((i[4], i[1]))
return praep_button #Just formating result from read_db in tuple(15x2)
def blub(self):
return forms.ChoiceField(widget=forms.RadioSelect, choices=self.praep_button)
myfield = forms.ChoiceField(widget=forms.RadioSelect, choices=praep_button) #print --><django.forms.fields.ChoiceField object at 0x751f9b90>
def myfield_choices(self):
field = self['myfield']
"""i think here is the problem.
Above 'myfield' is a django.forms.fields.ChoiceField object, but here it is rendered to html (like it should be). I have the code from https://stackoverflow.com/questions/6766994/in-a-django-form-how-do-i-render-a-radio-button-so-that-the-choices-are-separat.
But instead i should use field = self.felder (radioselect woth tuple of the db)"""
widget = field.field.widget
attrs = {}
auto_id = field.auto_id
if auto_id and 'id' not in widget.attrs:
attrs['id'] = auto_id
name = field.html_name
return widget.render(name, field.value(), attrs=attrs)
#return widget.get_renderer(name, field.value(), attrs=attrs)
So all in all I hope the problem is clear.
If i am using AlternativForm() i get the constant form. Instead i would like to get a dynamic form. If I access in views.py:
alternative = AlternativForm(optype = op, wsgroup = ab)
alternative = alternativ.felder
than I get . Can I render that to html?
If I set in forms.py:
field = self.felder
than I get the error that it is a field and not a widget
Thank you for reading!
You just need to assign the choices in the form's __init__() method. Almost what you're doing, but instead of defining self.felder to be a field, you need to use the already initialised form's fields:
myfield = forms.ChoiceField(widget=forms.RadioSelect, choices=praep_button)
def __init__(self, optype, wsgroup, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['myfield'].choices = self.get_choices(optype, wsgroup) # create your choices in this method
def get_choices(optype, wsgroup):
# call your other methods here
return praep_button

Migrate Function Based Views to Class Based views

According to this post, I'm trying to modify my whole script in order to get Class Based Views (CBV) in my Django application.
I would like to get any help, because it's the first time I'm using CBV.
My previous script function looks like this :
#login_required
def IdentityIndividuForm(request) :
success = False
query_Nom_ID = query_Prenom_ID = query_VilleNaissance_ID = None
if 'recherche' in request.GET:
query_Nom_ID = request.GET.get('q1NomID')
query_Prenom_ID = request.GET.get('q1PrenomID')
query_VilleNaissance_ID = request.GET.get('q1VilleNaissanceID')
sort_params = {}
lib.Individu_Recherche.set_if_not_none(sort_params, 'Nom__icontains', query_Nom_ID)
lib.Individu_Recherche.set_if_not_none(sort_params, 'Prenom__icontains', query_Prenom_ID)
lib.Individu_Recherche.set_if_not_none(sort_params, 'VilleNaissance__icontains', query_VilleNaissance_ID)
query_ID_list = Individu.objects.filter(**sort_params)
else :
query_ID_list = Individu.objects.none()
if request.method == 'POST':
form = IndividuFormulaire(request.POST or None, request.FILES or None)
if form.is_valid() :
post = form.save()
return HttpResponseRedirect(reverse('IndividuResume', kwargs={'id': post.id}))
else :
form = IndividuFormulaire()
form.fields['Utilisateur'].initial = request.user.last_name + " " + request.user.first_name
context = {
"form" : form,
"Individu" : Individu,
"query_Nom_ID" : query_Nom_ID,
"query_Prenom_ID" : query_Prenom_ID,
"query_VilleNaissance_ID" : query_VilleNaissance_ID,
"query_ID_list" : query_ID_list,
}
return render(request, 'Identity_Individu_Form.html', context)
I had a GET part and a POST part in my function but both part are independent. The first one lets to make a research over my database. The second one lets to create an object to my database.
My question is : How I can overwrite the GET part with function based on CBV ?
My new function form_valid(self, form) works well, but I don't overcome to migrate the GET part to my CBV part.
Thank you!
You can try it, first get super context data, after it do processing as you wish and after it update the contest data with yours
by default CreateView is subclass of the ProcessFormView that provide get method, as:
def get(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
return self.render_to_response(self.get_context_data(form=form))
so you can try to override the get method with return as:
return self.render_to_response(self.get_context_data(request, form=form))
and the get_context_data
def get_context_data(self, **kwargs) :
context_data = super(IdentityIndividuFormView, self).get_context_data(**kwargs)
if 'recherche' in self.request.GET:
query_Nom_ID = self.request.GET.get('q1NomID')
query_Prenom_ID = self.request.GET.get('q1PrenomID')
query_VilleNaissance_ID = self.request.GET.get('q1VilleNaissanceID')
sort_params = {}
lib.Individu_Recherche.set_if_not_none(sort_params, 'Nom__icontains', query_Nom_ID)
lib.Individu_Recherche.set_if_not_none(sort_params, 'Prenom__icontains', query_Prenom_ID)
lib.Individu_Recherche.set_if_not_none(sort_params, 'VilleNaissance__icontains', query_VilleNaissance_ID)
query_ID_list = Individu.objects.filter(**sort_params)
context_data['queryset'] = query_ID_list
else :
query_ID_list = Individu.objects.none()
context_data['queryset'] = query_ID_list
return context_data
I see another issue here, that you missed out #login_required part of the check here, to do so use the mixin like this :
from django.contrib.auth.mixins import LoginRequiredMixin
class IdentityIndividuFormView(LoginRequiredMixin, CreateView) :
And I suppose in case of GET request you can update the context using this:
def get(self, request, *args, **kwargs):

django reusable redirect best practice

I've got many views that need the same functionality so I'm trying to move that logic into a separate function (not a view function). The function finds a value either in the GET or session and returns a model instance OR redirects to a new page (kind of like forcing a login). The problem is that you can't redirect from a called function (that I know of). How should I handle this scenario?
Here's my code:
# This is the called function
def getActiveShowOrRedirect(request):
show_pk = request.GET.get('s', False)
if not show_pk:
show_pk = request.session.get('show_pk', False)
if not show_pk:
return HttpResponseRedirect('/setup/')
active_show = Show.objects.get(pk=show_pk)
return active_show
def overview(request):
active_show = getActiveShowOrRedirect(request)
scenes = Scene.objects.filter(show=active_show)
scenes = sorted(scenes, key=lambda s: s.name)
if request.method == 'POST':
form = SceneForm(request.POST)
if form.is_valid():
name = form.cleaned_data['name']
desc = form.cleaned_data['description']
scene = Scene(name=name.lower(), show=active_show, description=desc, creator=request.user)
scene.save()
return HttpResponseRedirect('/overview/')
else:
form = SceneForm(initial={'creator':request.user,'show':active_show})
return render_to_response('vfx_app/overview.html', {'active_show':active_show,'scenes':scenes,'form':form}, context_instance=RequestContext(request))
I suppose I could check the return type in the view function, but that seems kinda messy.
How about a decorator?
def requires_active_show(view):
"The decorated view must take active show as a second argument."
def wrapped(request, *args, **kw):
show_pk = request.GET.get('s') or request.session.get('show_pk')
if not show_pk:
return HttpResponseRedirect('/setup/')
return view(request, Show.objects.get(pk=show_pk), *args, **kw)
return wrapped
#requires_active_show
def overview(request, active_show):
scenes = Scene.objects.filter(show=active_show).order_by('name')
if request.method == 'POST':
form = SceneForm(request.POST)
if form.is_valid():
name = form.cleaned_data['name']
desc = form.cleaned_data['description']
scene = Scene.objects.create(
name=name.lower(),
show=active_show,
description=desc,
creator=request.user)
return HttpResponseRedirect('/overview/')
else:
form = SceneForm(initial={'creator': request.user, 'show': active_show})
return render('vfx_app/overview.html', {
'active_show': active_show,
'scenes': scenes,
'form': form
})