THE GOAL: Display a request-specific, read-only user_id on a django admin form when creating and updating a resource.
This display should be a readonly field (readonly, NOT disabled). The user_id displayed is derived from the requesting user (request.user) so the initial value is set from that.
Following this post, we should simply set it in the get_form method like so:
def get_form(self, request, obj=None, *args, **kwargs):
form = super(ProfileAdmin, self).get_form(request, *args, **kwargs)
form.base_fields['user'].initial = request.user
return form
However, user is no longer on the base_fields when it is readonly. In fact, I can't find it anywhere. Any thoughts?
Other posts suggest on save, which I intend to do, but I need it to show on the form before then.
You are right that making the field readonly excludes it from the base_fields.As mentioned in the docs :
Any fields in this option (which should be a list or tuple) will display its data as-is and non-editable; they are also excluded from the ModelForm used for creating and editing.
You could have define a method for it just like list_display but since you need to set user from the request, we need to have the request object, which isn't available in the method.
So for your use case, we can use formfield_for_foreignkey method as :
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'lend_by':
# setting the user from the request object
kwargs['initial'] = request.user.id
# making the field readonly
kwargs['disabled'] = True
return super().formfield_for_foreignkey(db_field, request, **kwargs)
Hope it helps.
Related
I have a CBV based on a TemplateView
It has a post(self) method to access user input.
Also, it has a template_name property which should be populated with data from post().
No Django models or forms are used here.
Attempt to extract data within post method:
# views.py
class ReturnCustomTemplateView(TemplateView):
def post(self, *args, **kwargs):
chosen_tmplt_nm = self.request.POST.get('tmplt_name')
print(chosen_tmplt_nm) # correct value.
# how do I use it outside this method? (like bellow)
template_name = chosen_tmplt_nm
... or is there any other way I can get the data from request.POST without def post()?
You can override the get_template_names() method [Django-doc] which returns an iterable (for example a list) of template names to search for when determining the name of the template, so:
class ReturnCustomTemplateView(TemplateView):
def get_template_names(self):
return [self.request.POST.get('tmplt_name')]
def post(self, *args, **kwargs):
return self.get(*args, **kwargs)
I would however advise to be careful with this: a POST request could be forged, so a hacker could make a POST request with a different template name, and thus try to obtain for example the content of the settings.py file or another file that contains sensitive data.
I've seen a similar question, alas it has not been answered.
I have an app that features Entries (like blog entries) which include a part called SubEntry. I want the users to be able to report SubEntries (i.e. press the button 'report', fill some fields and the application sends an email to admins, saving the report in db is nice to have):
The flow should be like that: at the view EntryDetails (url: /entry/entry-title/) the user may click on the SubEntry part. The modal opens and the subentry is visualized in the modal as enlarged, with a button/link underneath 'Report the SubEntry'. Then it's possible to click on the 'Report the SubEntry' button and two fields appear - reason of reporting and contact detail of the reporter (here I am just toggling the visibility of the fields). I manage to display the form (with get overriden - overriding get_form_kwargs causes the error No Entry with that title) but either the Entry or its attributes are not displayed...
My questions are:
1) is creating a model for Reporting (ReportSubEntry) a decent approach?
2) I can't seem to pass the needed variable (an Entry object that is to be a ForeignKey for a SubEntry object that is being created) from CreateReport view to the report_subentry.html.
any thoughts, advice? Python 3.5, Django 1.10
models.py:
class ReportSubentry(models.Model):
Entry = models.ForeignKey('Entry')
details = models.CharField(max_length=100)
contact = models.EmailField()
forms.py:
class ReportEntryForm(forms.ModelForm):
class Meta:
model = ReportSubEntry
fields = ['details', 'contact', 'project']
views.py:
class CreateReport(CreateView):
model = ReportSubEntry
form_class = ReportSubEntryForm
template_name = 'understand/report_subentry.html'
# tried two methods to pass the variables:
def get(self, request, *args, **kwargs):
self.object = None
title = kwargs.get('title')
kwargs['entry'] = get_object_or_404(Entry, title=title)
return super(CreateReport, self).get(request, **kwargs)
def get_form_kwargs(self, **kwargs):
title = kwargs.get('title')
kwargs['entry'] = get_object_or_404(Entry, title=title)
return kwargs
The current model that you are using ReportSubEntry is perfect and there is no need to change it.
In your forms.py ReportEntryForm you have to use relatedfields to be able to correctly serialize the data. There is no need to override anything. When user clicks on report the sub entry you have to pass the pk of Entry model as it is required to know which entry is reported. I am assuming that since you are successfully displaying the entries pk of those are present. When you receive the pk with other two fields you get the corresponding entry for pk and then pass the object to ReportSubentry.objects.create method.
The reportentry form should not contain foreign key. You have two choices for that. First is remove that field and pass the pk of entry from frontend using ajax calls or use javascript to add a disabled input field which contains pk of entry when user clicks on report subentry.
Ok, so I've solved this issue.
The only solution that worked for me was overriding the get method of the ReportSubentry without calling the get method of the superclass:
def get(self, request, *args, **kwargs):
self.object = None
title = kwargs.get('title')
entry = get_object_or_404(Entry, title=title)
context_data = self.get_context_data()
context_data.update(entry=entry)
return self.render_to_response(context_data)
Please feel free to discuss it.
I am kind of new to Django, and i am trying to make sort of a news website where users can submit articles(with an account) but the admin needs to check them before they can be posted. Is that possible?
Yes, it is.
The simplest approach would be creating simple flag in model let's say a Boolean field named verified, which by default would be False. You could add permissions. So in the end you could overwrite a function in your admin form, and show the field for superuser only.
class MyUserAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
self.exclude = []
if not request.user.is_superuser:
self.exclude.append('Permissions') #here!
return super(MyUserAdmin, self).get_form(request, obj, **kwargs)
I have a Model on Django-rest-framework, and I need to check every time a field on that Model was updated in the Django-Admin in order to do a update in another model.
How and where can I check it?
Thanks
#ssice is right you can utilise Django Signals, along with something like django-dirtyfields.
Or
If it's a one time thing, you can roll your own dirty field checker for that model by overriding model's __init__() and save() methods. Something like this (of course it can be much more complex depending on your requirements):
def __init__(self, *args, **kwargs):
super(YOUR_MODEL, self).__init__(*args, **kwargs)
# SAVE THE INITIAL VALUE
self.__original_value = self.value_you_want_to_track
def save(self, *args, **kwargs):
# Compare the initial value with the current value
if self.__original_value != self.value_you_want_to_track:
# DO SOMETHING, MAYBE TRIGGER SIGNAL
super(YOUR_MODEL, self).save(*args, **kwargs)
# Finally update the initial value after the save complete
self.__original_value = self.value_you_want_to_track
CAUTION
These would NOT work if you use model update(), as it does not trigger django's save() or related signals. But you said you want to track the changes made from the admin site, so I'm assuming this is not a problem.
If you only need to watch changes in Django Admin change form, you can hook the save_model() method of your ModelAdmin.
class YourAdmin(ModelAdmin):
def save_model(self, request, obj, form, change):
super().save_model(request, obj, form, change)
# do what you have to do here
You may also want to enclose this in a transaction to ensure the model is not saved if the other operation failed.
class YourAdmin(ModelAdmin):
#transaction.atomic
def save_model(self, request, obj, form, change):
super().save_model(request, obj, form, change)
# do what you have to do here
I want to prevent logged-in users to access login and register forms.
I've build custom mixin, but it isn't working. The problem is that even if the user is logged in, he can access login and register forms instead of beeing redirected to homepage.
My Mixin
class MustBeAnonymousMixin(object):
''' Only anonymous users (not logged in) may access login and register
'''
def dispath(self, *args, **kwargs):
if not self.request.user.is_anonymous:
return redirect(reverse('homepage'))
return super(MustBeAnonymousMixin, self).dispatch(*args, **kwargs)
LoginFormView
class LoginFormView(MustBeAnonymousMixin, TemplateView):
'''
Display basic user login form
'''
template_name = 'members/login.html'
def get_context_data(self, **kwargs):
context = super(LoginFormView, self).get_context_data(**kwargs)
context['login_form'] = UserLoginForm()
return context
I'm using Django 1.8. What am I doing wrong?
For another case where mixin does not work:
Remember: "Mixin param" must stand before "GenericView param"
Correct:
class PostDelete(LoginRequiredMixin, generic.DeleteView):
Incorrect:
class PostDelete(generic.DeleteView, LoginRequiredMixin):
Fix the typo in dispath and use is_authenticated() instead of is_anonymous (as indicated in the previous answer already)
is_anonymous should be a function call, and you probably should not use it:
is_anonymous()
Always returns False. This is a way of differentiating User and
AnonymousUser objects. Generally, you should prefer using is_authenticated() to this method.