BooleanField default value not set when creating a model instance - django

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.

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'),
...
]

How to use a form with autocomplete fields in django admin update action

I am using a custom form in admin panel with two autocomplete fields among the others.
My problem is that I don't know how to use the form in update action in order the stored data to appear with the autocomplete functionality.
In my implementation in update action the values appearing without autocomplete functionality.
How can I fix that?
my form
class ModelSeoMetadatumForm(forms.ModelForm):
name = ModelChoiceField(
required=False,
queryset=MetaTag.objects.exclude(name__isnull=True).values_list('name', flat=True).distinct(),
widget=autocomplete.ModelSelect2(url='seo:name-autocomplete')
)
property = ModelChoiceField(
required=False,
queryset=MetaTag.objects.exclude(property__isnull=True).values_list('property', flat=True).distinct(),
widget=autocomplete.ModelSelect2(url='seo:property-autocomplete')
)
class Meta:
model = ModelSeoMetadatum
fields = ('name', 'content', 'property', 'content_type', 'object_id')
my admin
#admin.register(ModelSeoMetadatum)
class ModelSeoMetadatumAdmin(admin.ModelAdmin):
add_form = ModelSeoMetadatumForm
list_display = ('id', 'name', 'content', 'property', 'content_object')
fields = ('name', 'content', 'property', 'content_type', 'object_id')
def get_form(self, request, obj=None, **kwargs):
defaults = {}
if obj is None:
defaults['form'] = self.add_form
defaults.update(kwargs)
return super().get_form(request, obj, **defaults)
You should overwrite the widget and give it the admin site as parameter.
admin class:
class MyAdmin(admin.ModelAdmin):
form = MyForm
form definition:
class MyForm(forms.ModelForm):
class Meta:
widgets = {
'some_lookup_field': AutocompleteSelect(
MyModel._meta.get_field('some_lookup_field').remote_field,
admin.site,
attrs={'style': 'width: 20em'},
),
}
Note, you need to have at lease one search_filter in the admin definition of your lookup field.
Have a look here for an improved version that expands if needed link

Django: Add extra attributes to form fields generated by UpdateView

Am using a custom user that is a subclass of Django AbstractUser, what am trying to archive is to allow user update their data everything works but the form look ugly. Below is my code the class attribute is not added to the form.
forms.py(simplified)
class AccountEditForm(forms.ModelForm):
class Meta:
model = CustomUser
fields = ('first_name', 'last_name', 'phone_number', 'date_of_birth', 'country')
widget = {
'first_name':forms.TextInput(
attrs={
'class': 'input-bordered',
}
)
}
views.py
class UserAccountDetails(LoginRequiredMixin, UpdateView):
template_name = 'dashboard/account_edit.html'
context_object_name = 'form'
form_class = AccountEditForm
model = CustomUser
def get_object(self, queryset=None):
"""
Return the object the view is displaying.
"""
if queryset is None:
queryset = self.get_queryset()
#Get logged in user from request data
queryset = queryset.filter(pk=self.request.user.id)
try:
# Get the single item from the filtered queryset
obj = queryset.get()
except queryset.model.DoesNotExist:
raise Http404(_("No %(verbose_name)s found matching the query") %
{'verbose_name': queryset.model._meta.verbose_name})
return obj
The widgets option is for overriding the defaults on explicitly declared fields. To add class to the field you have many options.
Option #1: Explicitly declare form field and add class through widgets in Meta.
class AccountEditForm(forms.ModelForm):
first_name = forms.TextField(widget=forms.TextInput())
class Meta:
model = CustomUser
fields = ('first_name', 'last_name', 'phone_number', 'date_of_birth', 'country')
widgets = {
'first_name': forms.TextInput(
attrs={
'class': 'input-bordered',
}
)
}
Option #2: Shorter version of option #1.
class AccountEditForm(forms.ModelForm):
first_name = forms.TextField(widget=forms.TextInput(attrs={'class': 'input-bordered'}))
class Meta:
model = CustomUser
...
Option #3: Add class in form's __init__ method.
class AccountEditForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(AccountEditForm, self).__init__(*args, **kwargs)
self.fields['first_name'].widget.attrs['class'] = 'input-bordered'
Option #4: Use django-widget-tweaks plugin.

Django: Make certain fields in a ModelForm required=False

How do I make certain fields in a ModelForm required=False?
If I have:
class ThatForm(ModelForm):
class Meta:
widgets = {"text": Textarea(required=False)}
Or if I have:
class ThatForm(ModelForm):
text = Textarea(required=False)
Django returns:
__init__() got an unexpected keyword argument 'required'
following from comments. Probably yes:
class ThatForm(ModelForm):
def __init__(self, *args, **kwargs):
# first call parent's constructor
super(ThatForm, self).__init__(*args, **kwargs)
# there's a `fields` property now
self.fields['desired_field_name'].required = False
you ought to add blank=True to the corresponding model
The documentation says
If the model field has blank=True, then required is set to False on the form field. Otherwise, required=True.
Also see the documentation for blank itself.
When we need to set required option on a bunch of fields we can:
class ThatForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.Meta.required:
self.fields[field].required = True
class Meta:
model = User
fields = (
'email',
'first_name',
'last_name',
'address',
'postcode',
'city',
'state',
'country',
'company',
'tax_id',
'website',
'service_notifications',
)
required = (
'email',
'first_name',
'last_name',
'address',
'postcode',
'city',
'country',
)
the following may be suitable
class ThatForm(ModelForm):
text = forms.CharField(required=False, widget=forms.Textarea)
You could try this:
class ThatForm(ModelForm):
class Meta:
requireds =
{
'text':False,
}
requireds must be under Meta.

Access model object in admin.StackedInline inline

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.