Pass Variable to Django Admin Form - python

I have an add form for CalibrationCertificates in my django admin site. If I link to it from a non-admin template, Instrument_Detail.html, is it possible to pass context information as a default value to the add form.
That is, the only choice in the add form is which Instrument the certificate is for. As the link is already associated with an instrument, is there a way to pass that value, such that the add certificate form will default to the instrument the user came from?
My ModelAdmin is as follows:
class CertificateAdmin(admin.ModelAdmin):
exclude = ('issued_by', 'expires',)
def save_model(self, request, obj, form, change):
obj.issued_by = request.user
obj.expires= datetime.date.today() + datetime.timedelta(days=obj.instrument.kind.duration)
obj.save()

Not sure if I understand your question correctly but I think this is what you want:
def add_view(self, request, form_url='', extra_context=None):
extra_context = extra_context or {}
extra_context['my_extra_content'] = self.something
return super(MyModelAdmin, self).add_view(request, form_url,
extra_context=extra_context)

Related

Class Based Views Form neither Valid nor Invalid (Django)

I'm new to Django Class Based Views and I can't get my form to pass through neither form_valid() nor form_invalid().
I have taken most of this code from the Django allauth module, so I extend some mixins (AjaxCapableProcessFormViewMixin & LogoutFunctionalityMixin) that I do not know well.
This form is meant to allow users to change their passwords upon receiving an email. As it is now, users are able to change their password but since the form_valid() function is never triggered, they do no get redirected to the success URL as is intended. Instead the password change is registered but the users stay on the same page.
The functions dispatch(), get_form_kwargs() & get_form_class() are all triggered and behave in the way that they should. Still, it's unclear to me why they execute in the order that they do (dispatch() is triggered first, then get_form_class() and finally get_form_kwargs(). I suppose they implicitely have an order as presented in this documentation: https://ccbv.co.uk/projects/Django/4.0/django.views.generic.edit/FormView/)
I am lacking some intuition about how this works, therefore I don't know if there is a way to redirect to the success URL without passing through form_valid() because that would also solve my problem.
As is mentionned in the title, neither form_valid() nor form_invalid() is triggered after submitting a new password. The last executed bit of code is the return kwargs from the get_form_kwargs() function.
Here is my code:
class PasswordResetFromKeyView(AjaxCapableProcessFormViewMixin, LogoutFunctionalityMixin, FormView):
template_name = "account/password_reset_from_key." + app_settings.TEMPLATE_EXTENSION
form_class = ResetPasswordKeyForm
success_url = '/'
reset_url_key = "set-password"
def get_form_class(self):
return get_form_class(
app_settings.FORMS, "reset_password_from_key", self.form_class
)
def dispatch(self, request, uuid, **kwargs):
self.request = request
token = get_object_or_404(ResetToken, token=uuid)
if token.redeemed == False:
self.reset_user = token.client
self.token = token
response = self.render_to_response(self.get_context_data(token_fail=False))
else:
return super(PasswordResetFromKeyView, self).dispatch(
request, uuid, **kwargs
)
return response
def get_form_kwargs(self, **kwargs):
kwargs = super(PasswordResetFromKeyView, self).get_form_kwargs(**kwargs)
kwargs["user"] = self.reset_user
if len(kwargs) > 3:
try:
if kwargs['data']['password1'] == kwargs['data']['password2']:
self.reset_user.set_password(kwargs['data']['password1'])
self.reset_user.save()
self.token.redeemed = True
self.token.date_redeemed = datetime.now()
self.token.save()
perform_login(
self.request,
self.reset_user,
email_verification=app_settings.EMAIL_VERIFICATION,
)
else:
pass
##passwords dont match
except:
##couldnt change the password
pass
return kwargs
def form_valid(self, form, **kwargs):
form.save()
return super(PasswordResetFromKeyView, self).form_valid(form)
def form_invalid(self, form):
response = super().form_invalid(form)
if self.request.accepts('text/html'):
return response
else:
return JsonResponse(form.errors, status=400)
If both methods are not triggered, it means - you requests.method is never is 'POST'.
The class FormView calls this two methods only if post.method == 'POST':
# code from django.views.generic.edit
...
def post(self, request, *args, **kwargs):
"""
Handle POST requests: instantiate a form instance with the passed
POST variables and then check if it's valid.
"""
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
By the way in dispatch, if token.redeemed == False you should return self.form_invalid().

How to return an empty form in ModelFormMixin

DetailStory subclasses DetailView and ModelFormMixin thus presenting the DetailView of a certain story and a form at the end. However, on filling the form and submitting the data, the data is saved in the databases but it is still shown on the form (in addition to the one now displayed on the DetailView). How do I present an empty form after submitting it? (Here is the code sample)
class DetailStory(DetailView, ModelFormMixin):
model = Story
template_name = 'stories/detail_story.html'
context_object_name = 'detail'
form_class = CommentForm
def get(self, request, *args, **kwargs):
self.object = None
self.form = self.get_form(self.form_class)
return DetailView.get(self, request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.object = None
self.form = self.get_form(self.form_class)
if self.form.is_valid():
obj = self.form.save(commit=False)
obj.user = self.request.user
obj.memoir = self.get_object()
self.object = obj.save()
return self.get(request, *args, **kwargs)
def get_object(self):
item_id = crypt.decode(self.kwargs['story_id'])[0]
obj = get_object_or_404(Story, Q(privacy='public') | Q(user_id=self.request.user.id), pk=item_id)
return obj
get_form uses the request data to construct the form as per the docs
If the request is a POST or PUT, the request data (request.POST and request.FILES) will also be provided.
So simply don't make your post function go back through the get, just have it redirect to your required place or do anything differently to pointing it at the get function.
return redirect('mynamespace:story_detail', story_id=self.object.pk)
You may wish to read this answer for a list of technical details you should consider whilst making your application. In particular,
Redirect after a POST if that POST was successful, to prevent a refresh from submitting again.

Django: How to provide context into a FormView get() method (also using request param)

I'm trying to provide some additional context into the get() method in my FormView. I need get() because I need to run some logic first, check for a potential redirect. I also need access to the request object (because I need to check session data). Can't figure out how to do it. Simplified code below..
Attempt 1:
class LoginView(FormView):
template_name = 'members/login.html'
form_class = LoginForm
def get(self, request):
# check if to redirect
if self.request.session.get('user'):
return redirect('/dashboard/')
# render page with extra context
else:
context = super(LoginView, self).get(request)
context['message'] = self.request.session['message']
return context
No errors, but context does not come through in the template.
Attempt 2:
class LoginView(FormView):
template_name = 'members/login.html'
form_class = LoginForm
def get_context_data(self, request, **kwargs):
# check if to redirect
if self.request.session.get('user'):
return redirect('/dashboard/')
# render page with extra context
else:
context = super(LoginView, self).get_context_data(**kwargs)
context['message'] = self.request.session['message']
return context
Getting TypeError: get_context_data() takes exactly 2 arguments (1 given)
P.S. This work relates to a workaround Django's buggy messages middleware which seems to be working locally flawlessly but on live (Heroku) is not 100% reliable, renders on some pages only. Ugh, frustration setting in...
Ditch the request argument to the get_context_data method. You should also use the dispatch method to check if the user is logged in.
class LoginView(FormView):
template_name = 'members/login.html'
form_class = LoginForm
def dispatch(self, *args, **kwargs):
"""Use this to check for 'user'."""
if request.session.get('user'):
return redirect('/dashboard/')
return super(LoginView, self).dispatch(*args, **kwargs)
def get_context_data(self, **kwargs):
"""Use this to add extra context."""
context = super(LoginView, self).get_context_data(**kwargs)
context['message'] = self.request.session['message']
return context

Django redirect in admin failed

After saving an object by clicking saveasnew button in admin interface i want to redirect to another url:
admin.py
class AirplanesAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.save()
if "_saveasnew" in request.POST:
self.message_user(request, 'YAY!!!')
return HttpResponseRedirect(reverse('admin:routes_airplanes_change',
args=(obj.pk,),
current_app=self.admin_site.name))
The "YAY" message is showed... but django redirects me in a list of pbject pages, not to a new object page... Don't work even this code:
admin.py
class AirplanesAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.save()
if "_saveasnew" in request.POST:
self.message_user(request, 'YAY!!!')
redirect('http://example.com')
I see "YAY", but it redirects me to a list object again... what am I doing wrong ?
save_model return None. and It's result is not used anywhere. So doesn't matter what is returned (redirect(...) or None)
I think
def response_add(self, request, new_object):
pass
and
def response_change(self, request, new_object):
pass
are what You are looking for.
If we look into django source code we will see following:
def response_change(self, request, obj):
"""
Determines the HttpResponse for the change_view stage.
"""
and
def response_add(self, request, obj, post_url_continue=None):
"""
Determines the HttpResponse for the add_view stage.
"""

Is it possible to override the method used to call Django's admin delete confirmation page?

On Django's admin pages, I'd like to perform an action when the administrator clicks the Delete button for an object. In other words, I'd like to execute some code prior to arriving on the "Are you sure?" delete confirmation page.
I realize I could override the template page for this object, but I was hoping for something easier (i.e., override a method on the model or the form).
Any thoughts?
You can override ModelAdmin.delete_view() method, like:
class MyModelAdmin(ModelAdmin):
def delete_view(self, request, object_id, extra_context=None):
# if request.POST is set, the user already confirmed deletion
if not request.POST:
perform_my_action()
super(MyModelAdmin, self).delete_view(request, object_id, extra_context)
We can use django.shortcuts.redirect to interrupt deletion, like this:
def check_del(self, object_id):
produkt = Produkt.objects.get(id = object_id)
if produkt.typsklepu_set.all():
return False
else:
return True
def delete_view(self, request, object_id, extra_context=None):
# if request.POST is set, the user already confirmed deletion
if not request.POST and self.check_del(object_id):
return super(ProduktAdmin, self).delete_view(request, object_id, extra_context)
elif request.POST:
return super(ProduktAdmin, self).delete_view(request, object_id, extra_context)
else:
msg = u'Can not delete this object.'
messages.error(request, msg)
return redirect('..')

Categories

Resources