I want to get the current user logged in my forms.py class for show his biography in a CharField (with placeholder or value)
i've tried the init method but it doesn't work for me idk what i'm doing wrong ...
here is my form class
class ProfileSettingsForm(forms.Form):
avatar = forms.ImageField(required=False)
description = forms.CharField(required=False, max_length=230, widget=forms.Textarea(attrs={"value": "here i want the description of the current user"}))
with init
class ProfileSettingsForm(forms.Form):
def __init__(self, user, *args, **kwargs):
self.user = user
super(ProfileSettingsForm, self).__init__(*args, **kwargs)
avatar = forms.ImageField(required=False)
description = forms.CharField(required=False, max_length=230, widget=forms.Textarea(attrs={"value": self.user}))
I tried this method
class ProfileSettingsForm(forms.Form):
user = None
def __init__(self, user, *args, **kwargs):
self.user = user
super(ProfileSettingsForm, self).__init__(*args, **kwargs)
avatar = forms.ImageField(required=False)
description = forms.CharField(required=False, max_length=230, widget=forms.Textarea(attrs={"value": self.user}))
but it return None
in my views.py:
form = forms.ProfileSettingsForm(user=request.user)
If you're using Class-Based Views you can override the get_form_kwargs method.
views.py
# View might not be the correct class for you to inherit from, it's more a example. Having said that, you'd probably use CreateView
class YourView(View):
def get_form_kwargs(self):
form = super().get_form_kwargs()
form['user'] = self.request.user
return form
forms.py
class ProfileSettingsForm(forms.Form):
def __init__(self, user, *args, **kwargs):
self.user = user
super(ProfileSettingsForm, self).__init__(*args, **kwargs)
avatar = forms.ImageField(required=False)
description = forms.CharField(required=False, max_length=230, widget=forms.Textarea(attrs={"value": self.user}))
Related
I have a custom User class with a property that return a queryset
And I have an Admin class that use a custom ModelForm with 2 ModelChoiceField and fone BooleanFields.
I want to filter queryset of one ModelChoiceField using user property
but my problem is that I do not have access to request or user in my ModelForm.
I try to use method get_form_kwargs I use for 'normal CBV' but it doen't work as this method do not exist in ModelAdmin
admin.py
class User_TableAdmin(SimpleHistoryAdmin):
def __init__(self, model, admin_site):
super(User_TableAdmin,self).__init__(model,admin_site)
self.form.admin_site = admin_site # capture the admin_site
form = User_TableAdminForm **# How to request object to my form?**
list_display = ('id','user','table','can_download')
search_fields = ('user','table','can_download')
forms.py
class User_TableAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(User_TableAdminForm, self).__init__(*args, **kwargs)
# add the 'green +' button to create a new user (green + button suppress when overidding field with ModelChoiceField)
self.fields['user'].widget = RelatedFieldWidgetWrapper(
self.fields['user'].widget,
self.instance._meta.get_field('user').remote_field,
admin_site)
class Meta:
model = User_Table
fields = '__all__'
# display only tables of study database that user workin: User property
# tables = self.user.can_download
tables = Table.objects.all() **#<- I would like to use something like request.user.can_download**
user = forms.ModelChoiceField(queryset = User.objects.all(), label = "User", widget = forms.Select())
table = forms.ModelChoiceField(queryset = tables, label = "Table", widget = forms.Select())
can_download = forms.BooleanField(
widget = forms.CheckboxInput(),
required = False,
)
models.py
class User(AbstractUser):
# site = models.ForeignKey(Site, on_delete = models.CASCADE, related_name="database")
birth_date = models.DateField(null=True, blank=True)
#property
def can_download(self):
""" Return the related list of tables use can download. """
return Table.objects.filter(
Q(database__study__in = [uss.study.id for uss in User_Site_Study.objects.filter(user = self.id)]) &
Q(database__study__is_opened = True) &
Q(database__is_opened = True)
)
can_download.fget.short_description = 'List of tables user allowed to download'
For that you can override ModelAdmin.get_form() which returns the ModelForm class that will be used in add or change admin page and decorate it to inject request upon creation of form instance.
class ModelFormWithRequest(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
super().__init__(*args, **kwargs)
#classmethod
def inject_request(cls, request):
def __new__(_, *args, **kwargs):
kwargs.setdefault('request', request)
return cls(*args, **kwargs)
return type(
f'{cls.__name__}Decorator',
(cls,),
{
'__module__': cls.__module__,
'__doc__': cls.__doc__,
'__new__': __new__
}
)
class User_TableAdminForm(ModelFormWithRequest):
def __init__(self, *args, **kwargs):
super(ModelFormWithRequest, self).__init__(*args, **kwargs)
self.fields['table'].queryset = # set new queryset filtered with self.request.user data
# add the 'green +' button to create a new user (green + button suppress when overidding field with ModelChoiceField)
self.fields['user'].widget = RelatedFieldWidgetWrapper(
self.fields['user'].widget,
self.instance._meta.get_field('user').remote_field,
admin_site)
class User_TableAdmin(SimpleHistoryAdmin):
form = User_TableAdminForm
list_display = ('id','user','table','can_download')
search_fields = ('user','table','can_download')
def __init__(self, model, admin_site):
super(User_TableAdmin,self).__init__(model,admin_site)
self.form.admin_site = admin_site # capture the admin_site
# this is how you pass request to form
def get_form(self, request, obj=None, change=False, **kwargs):
ModelForm = super().get_form(request, obj=obj, change=change, **kwargs))
# pass request only to change form, it assumes this is a subclass of auth.UserAdmin
if change:
return ModelForm.inject_request(request)
return ModelForm
just to extend answer, if one needs to pass request to InlineModelAdmin's forms, which is handled by formsets, need to override get_formset() instead of get_form()
class CustomInlineModelAdmin(admin.InlineModelAdmin):
def get_formset(self, request, obj=None, **kwargs):
formset_class = super().get_formset(request, obj=obj, **kwargs)
formset_class.form = formset_class.form.inject_request(request)
return formset_class
There is a shorter solution, just set ModelForm.request = request in ModelAdmin.get_form(). In this case ModelFormWithRequest is not needed, but I prefer to pass dynamic dependencies though constructor instead of appending it to class objects which are global.
I have two classes Organisation, and Staff in addition to the User class. I want to create a mixin called "UserIsAdminMixin" that checks whether the user logged in has the role "admin" in a specific Organisation.
The classes (simplified)
class Organisation(models.Model):
name = models.CharField(max_length=50)
class Staff(models.Model):
class Role(models.TextChoices):
ADMIN = 'ADMIN', "Admin"
STAFF = 'STAFF', "Staff"
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, db_index=True, related_name="staff_profiles", on_delete=models.SET_NULL)
organisation = models.ForeignKey('organisations.Organisation', db_index=True, related_name="staff", on_delete=models.CASCADE)
role = models.CharField(max_length=10, choices=Role.choices, default=Role.STAFF)
I then currently have this for my UserIsAdmin mixin:
class UserIsAdminMixin:
def dispatch(self, request, *args, **kwargs):
if self.request.user.staff_profiles.filter(organisation=self.get_object(), role=Staff.Role.ADMIN):
return super().dispatch(request, *args, **kwargs)
else:
raise PermissionDenied
This works great for this view:
organisations.views.py (URL: organisation/<int:pk>)
class OrganisationDetail(LoginRequiredMixin, UserIsAdminMixin, generic.DetailView):
model = Organisation
template_name= "organisations/detail.html"
login_url = "login"
But I'd also like it to work for this view as well, which it obviously doesn't as self.get_object() returns a Staff object in this case when it's expecting an Organisation object:
staff.views.py (URL: organisation/<int:pk_alt>/staff/<int:pk>)
class StaffDetail(LoginRequiredMixin, UserIsAdminMixin, generic.DetailView):
model = Staff
template_name="staff/detail.html"
login_url = "login"
I was able to make changes to the mixin to get it to work in the second scenario but not the first:
class UserIsAdminMixin:
def dispatch(self, request, *args, **kwargs):
if self.request.user.staff_profiles.filter(organisation__pk=self.kwargs['pk_alt']), role=Staff.Role.ADMIN):
return super().dispatch(request, *args, **kwargs)
else:
raise PermissionDenied
So is there any way I can change the mixin so it works for both Organisation and Staff models to check that the User is a staff member with the role of admin for a given organisation?
Any help would be greatly appreciated!
Many Thanks,
GoingRoundInCircles
Well, a possible solution is to have a get_organisation_id() method in both your views.
Your mixin:
class UserIsAdminMixin:
def dispatch(self, request, *args, **kwargs):
organisation_id = self.get_organisation_id()
if self.request.user.staff_profiles.filter(organisation_pk=organisation_id, role=Staff.Role.ADMIN):
return super().dispatch(request, *args, **kwargs)
else:
raise PermissionDenied
StaffDetail:
class StaffDetail(LoginRequiredMixin, UserIsAdminMixin, generic.DetailView):
model = Staff
template_name="staff/detail.html"
login_url = "login"
def get_organisation_id(self):
return self.kwargs.get('pk_alt')
OrganisationDetail:
class OrganisationDetail(LoginRequiredMixin, UserIsAdminMixin, generic.DetailView):
model = Organisation
template_name= "organisations/detail.html"
login_url = "login"
def get_organisation_id(self):
return self.get_object().id
I want to be able to edit registered User profile by using UpdateModelMixin class. The forms to edit is existed but when we want to PUT the new information , the new one is not applied and the pervious info is displayed.
models.py:
class Student(models.Model):
user = models.OneToOneField(CustomUser, on_delete=models.CASCADE)
entry_year = models.PositiveIntegerField()
student_no = models.PositiveIntegerField()
def get_full_name(self):
return self.user.first_name + self.user.last_name
def __unicode__(self):
return self.get_full_name()
views.py:
class ProfessorDetailAPIView(DestroyModelMixin, UpdateModelMixin, RetrieveAPIView):
queryset = Professor.objects.all()
serializer_class = ProfessorDetailSerializers
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
serializers.py:
class ProfessorDetailSerializers(serializers.ModelSerializer):
user = CustomUserSerializer()
professor_no = SerializerMethodField()
class Meta:
model = Student
fields = (
'user',
'professor_no',
)
def get_professor_no(self, obj):
return str(obj.professor_no)
There is not any changes applied on information
Here is the Model class
class Album(models.Model):
name = models.CharField(max_length=256)
public = models.BooleanField(default=False)
user = models.ForeignKey(get_user_model())
class Meta:
unique_together = (("name", "user"),)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('photos:index')
Here is the View
class Create(CreateView):
model = Album
fields = ['name', 'public']
form_class = AlbumCreateForm
def form_valid(self, form):
form.instance.user = self.request.user
return super(Create, self).form_valid(form)
And here is the form Class
class AlbumCreateForm(ModelForm):
class Meta:
model = Album
fields = ['name', 'public']
labels = {'name': '', 'public': 'Public'}
def __init__(self,*args, **kwargs):
super(AlbumCreateForm, self).__init__(*args, **kwargs)
self.fields['name'].widget = forms.TextInput(
attrs={'placeholder': 'name'})
I can create album just fine, but what I would like to do is prevent duplicate albums from being created for a particular user. For example if user1 has already created album1, he should not be able to create another album named album1.
The place where I can do it is AlbumCreateForm. But AlbumCreateForm does not have any knowledge of current user. Any idea how it can be accomplished?
Pass user to form - add him to form kwargs by adding this method to view:
def get_form_kwargs(self, *args, **kwargs):
kwargs = super(Create, self).get_form_kwargs(*args, **kwargs)
kwargs['user'] = self.request.user
return kwargs
Get user in form and check:
def __init__(self,*args, **kwargs):
self.user = kwargs.pop('user') # this line added
super(AlbumCreateForm, self).__init__(*args, **kwargs)
self.fields['name'].widget = forms.TextInput(
attrs={'placeholder': 'name'})
def clean(self):
if Album.objects.filter(user=self.user).exists():
raise forms.ValidationError('Error description')
I have a model along with a ModelForm based on that model. The ModelForm contains a ModelMultipleChoice field, which I specify in the subclass of my ModelForm:
class TransactionForm(ModelForm):
class Meta:
model = Transaction
def __init__(self, *args, **kwargs):
super(TransactionForm, self).__init__(*args, **kwargs)
self.fields['category'] = forms.ModelChoiceField(queryset=Category.objects.filter(user=user))
As you can see, I need to filter the Category queryset by user. In other words, users should only see their own categories on the drop down. But how can I do this when user, or more specifically, request.user, is not available in a Model instance?
Edit: Adding my subclass of the CBV:
class TransUpdateView(UpdateView):
form_class = TransactionForm
model = Transaction
template_name = 'trans_form.html'
success_url='/view_trans/'
def get_context_data(self, **kwargs):
context = super(TransUpdateView, self).get_context_data(**kwargs)
context['action'] = 'update'
return context
I tried form_class = TransactionForm(user=request.user) and I'm getting a NameError saying that request was not found.
You can pass request.user to form init in view:
def some_view(request):
form = TransactionForm(user=request.user)
and add user parameter to form __init__ method (or pop it from kwargs in form):
class TransactionForm(ModelForm):
class Meta:
model = Transaction
# def __init__(self, *args, **kwargs):
# user = kwargs.pop('user', User.objects.get(pk_of_default_user))
def __init__(self, user, *args, **kwargs):
super(TransactionForm, self).__init__(*args, **kwargs)
self.fields['category'] = forms.ModelChoiceField(
queryset=Category.objects.filter(user=user))
update: in class based views you can add extra parameter to form init in get_form_kwargs:
class TransUpdateView(UpdateView):
#...
def get_form_kwargs(self):
kwargs = super(YourView, self).get_form_kwargs()
kwargs.update({'user': self.request.user})
return kwargs