Access model object in admin.StackedInline inline - django

I am trying to use an inline in UserAdmin in admin.py
I am looking for a way to modify the fields of that inline based on the object.
ProfileInline
class ProfileInline(admin.StackedInline):
model = UserProfile
filter_horizontal = ('user_markets',)
fk_name = 'user'
max_num = 1
can_delete = False
fields = ('email_role', )
verbose_name_plural = 'Profile'
UserAdmin
class UserAdmin(UserAdmin):
list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff', roles, login)
list_filter = ('groups',)
inlines = (ProfileInline,)
Here I need to modify ProfileInline.fields = ('department','email_role') if the user belongs to the Sales Group, else whatever.
I need a way to access the user Object and update the fields.

class ProfileInline(admin.StackedInline):
model = UserProfile
filter_horizontal = ('user_markets',)
fk_name = 'user'
max_num = 1
can_delete = False
fields = ('email_role', )
verbose_name_plural = 'Profile'
def get_fieldsets(self, request, obj=None):
fieldsets = super(ProfileInline, self).get_fieldsets(request, obj)
# fieldsets[0][1]['fields'].remove('email_role')
fieldsets[0][1]['fields'] = ('department', 'email_role')
return fieldsets
get_fieldsets method is your solution. You have request object so request.user also.

Related

Email field with unique constraint prevents PUT method of serializer from saving. django rest_framework

When creating an object in my api view it seems fine but whenever I update the object even I didn't change my email field it says user with this Email Address already exists. I am using generics.RetrieveUpdateView for my view.
I thought generics.RetrieveUpdateView automatically handles this. What am I missing?
my model looks like this:
class User(AbstractUser):
email= models.EmailField(unique=True)
...
my serializer looks like this:
class UserListForProfileSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username','email','last_login', 'date_joined','is_active', 'is_student', 'is_staff', 'is_teacher', 'is_registrar', 'is_superuser']
read_only_fields = ('username','date_joined', 'last_login','is_active', 'is_student', 'is_staff', 'is_teacher', 'is_registrar', 'is_superuser')
class StaffProfileDetailSerializer(CreateProfileSerializer):
user = UserListForProfileSerializer(many= False)
class Meta:
model = StaffProfile
fields = ['user','first_name', 'middle_name', 'last_name', 'gender', 'employee_number', 'date_of_birth', 'mobile_number','dob_month','dob_day','dob_year','address',]
read_only_fields = ('date_joined', 'last_login',)
my view looks like this:
class UserProfileDetail(generics.RetrieveUpdateAPIView):
authentication_classes = [SessionAuthentication, BasicAuthentication]
permission_classes = [IsAuthenticated]
serializer_class = StaffProfileDetailSerializer
def get_queryset(self, *args, **kwargs):
userid = self.request.user.id
return StaffProfile.objects.filter(pk= userid)
urls.py:
urlpatterns = [
path('api/', apiOverview, name='apioverview'),
path('api/user/profile/detail/<int:pk>/', UserProfileDetail.as_view(),
name='userprofile-detail'),
...
]

Edit ManyToMany Inline fields with self relationship in Django Admin change form

How can I get a model's fields to be editable in the Inline in a ManyToMany relationship when the relationship is with itself?
class MyModel(models.Model):
children = models.ManyToManyField(
'self',
related_name='parents',
symmetrical=False
)
class MyModelChildAdminInline(admin.TabularInline):
"""Inline for the intermediate table"""
model = MyModel.children.through
# Number of child-forms shown at the bottom of the Profile detail view
extra = 0
fk_name = 'from_mymodel'
raw_id_fields = ("to_mymodel",)
fields = ('to_mymodel', 'field1', 'field2', 'field3', )
readonly_fields = ('field1', 'field2', 'field3',)
def field1(self, obj):
return obj.to_mymodel.field1
def field2(self, obj):
return obj.to_mymodel.field3
def field3(self, obj):
return obj.to_mymodel.field2
#admin.register(MyModel)
class MyModelAdmin(VersionAdmin):
inlines = (MyModelChildAdminInline, )
exclude = ('children', )

Django Admin conditional display inlines based on models property

I've got marketplace models with fields:
class VacationEvent(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
process_fba = models.BooleanField(default=True)
#property
def has_amazon_fba_vacation(self):
return hasattr(self, 'amazon_fba_vacation')
And in admin.py:
class FBAInline(admin.StackedInline):
model = AmazonFBAVacation
can_delete = False
verbose_name_plural = 'amazon fba vacation'
fk_name = 'event'
I need to display conditionally FBAInline class when creating\updating VacationEventAdmin. list_filter shows true\false values of "process_fba" field, so if it is true - FBAInline should be displayed:
#admin.register(VacationEvent)
class VacationEventAdmin(admin.ModelAdmin):
inlines = []
list_display = ('id', 'user', 'holiday_name', 'start_date', 'end_date', 'process_fba', 'process_fbm', 'process_walmart', 'process_ebay')
list_filter = ['process_fba', 'process_fbm', 'process_walmart', 'process_ebay', 'status',]
search_fields = ['holiday_name', 'user__username']
# if VacationEvent.process_fba and VacationEvent.has_amazon_fba_vacation:
# inlines.append(FBAInline)
# try:
# getattr(VacationEvent, 'process_fba')
# except AttributeError:
# print "doesn't"
# else:
# inlines.append(FBAInline)
I tried getting attr but don't understant how to compare field values for admin. Thanks for any help.
Override get_formsets_with_inlines method
get_formsets_with_inlines(self, request, obj=None):
for inline in self.get_inline_instances(request, obj):
if obj and obj.has_amazon_fba_vacation:
yield inline.get_formset(request, obj), inline

how to define a userprofile into UserDetialsSerializer?

I want to be able to access a userprofile instance through :
profile = instance.userprofile statement in UserSerializer
instance is created through:
instance = super(UserSerializer, self).update(instance, validated_data) statement in
UserSerializer
Since UserSerializer is inheriting UserDetailsSerializer, i think i should define a userprofile in UserDetailsSerializer.
But i dont know how to do it ?
Question: How to define userprofile in UserDetailsSerializer to achieve the above ?
UserSerializer:
class UserSerializer(UserDetailsSerializer):
company_name = serializers.CharField(source="userprofile.company_name")
class Meta(UserDetailsSerializer.Meta):
fields = UserDetailsSerializer.Meta.fields + ('company_name',)
def update(self, instance, validated_data):
profile_data = validated_data.pop('userprofile', {})
company_name = profile_data.get('company_name')
instance = super(UserSerializer, self).update(instance, validated_data)
# get and update user profile
profile = instance.userprofile
if profile_data and company_name:
profile.company_name = company_name
profile.save()
return instance
UserDetailsSerializer:
class UserDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = ('username','email', 'first_name', 'last_name')
read_only_fields = ('email', )
UserProfile model:
class UserProfile(models.Model):
user = models.OneToOneField(User)
# custom fields for user
company_name = models.CharField(max_length=100)
Do ask if more clarity is required?
I think you want a serializer methodfield to be part of your serializer? (I don't full understand your question);
class UserDetailsSerializer(serializers.ModelSerializer):
user_related = serializers.Field(source='method_on_userprofile')
class Meta:
model = UserProfile
fields = ('username','email', 'first_name', 'user_related', )
read_only_fields = ('email', 'user_related',)
I think I have answered similar one here
In the documentation it is assumed that userprofile was already created and now can be updated. You just need a check
# get and update user profile
try:
profile = instance.userprofile
except UserProfile.DoesNotExist:
profile = UserProfile()
if profile_data and company_name:

BooleanField default value not set when creating a model instance

When creating an instance of a model having a BooleanField my_boolean_field with a default set to True, I get an error:
my_boolean_field is required
Shouldn't it be set to the default value?
models.py
class MyModel(User):
my_boolean_field = models.BooleanField(default=False)
admin.py
class MyModelCreationForm(UserCreationForm):
my_boolean_field = forms.BooleanField(initial=False)
class Meta:
model = User
class MyModelChangeForm(UserChangeForm):
my_boolean_field = forms.BooleanField(initial=False)
class Meta:
model = User
class MyModelAdmin(UserAdmin):
form = MyModelChangeForm
add_form = MyModelCreationForm
list_filter = ()
list_display = ('username', 'my_boolean_field')
fieldsets = (
(None, {'fields': ('username', 'my_boolean_field', 'password' )}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'my_boolean_field', 'password1', 'password2')}
),
)
def get_form(self, request, obj=None, **kwargs):
form = super(MyModelAdmin, self).get_form(request, obj, **kwargs)
if obj==None:
form.base_fields['username'].widget.attrs['autocomplete'] = 'off'
form.base_fields['password1'].widget.attrs['autocomplete'] = 'off'
form.base_fields['password2'].widget.attrs['autocomplete'] = 'off'
return form
samsic_site.register(MyModel, MyModelAdmin)
Change field definition in your model form to specify require=False.
class MyModelCreationForm(UserCreationForm):
my_boolean_field = forms.BooleanField(initial=False, required=False)
class Meta:
model = User
Note on BooleanField reference
Note
Since all Field subclasses have required=True by default, the validation condition here is important. If you want to include a boolean in your form that can be either True or False (e.g. a checked or unchecked checkbox), you must remember to pass in required=False when creating the BooleanField.