I was making a django forms and there is a field owner which is related with ForeignKey by User model , Sometimes name of user is same so I want to search it by their email address , How can I add searching of email field in forms like this search_fields = ['email'].
class GroupForm(forms.ModelForm):
class Meta:
model = Group
fields = ('name', 'owner', 'club', 'moderator', 'group_type', 'country')
def __init__ (self, *args, **kwargs):
# brand = kwargs.pop("brand")
super(GroupForm, self).__init__(*args, **kwargs)
language_results = User.objects.all()
# self.fields["owner"].widget = forms.widgets.CheckboxSelectMultiple()
# self.fields["owner"].widget = autocomplete.ModelSelect2()
self.fields["owner"] = forms.ModelMultipleChoiceField(
queryset=User.objects.all(),
required=True,
widget = forms.SelectMultiple(attrs={
'placeholder': "Choose the users(s)",
'class': 'chzn-select',
'multiple tabindex': '6',
}))
you just use a CharField and add that form in your template;and after submiting that search you get the value entred by the user and use the objects filter to return the corresponding result.
Related
I have a model of Organisation and three models have Foreign keys to Organisation model. Three nested models is Users ( custom model ), Description and Contacts. Users has unique field email. Description has unique pair of two fields. I have custom serializer to Organisation.
class OrganisationSuperAdminSerializer(serializers.ModelSerializer):
users = UsersSerializer(many=True, required=False)
contacts = ContactsSerializer(many=True, required=False)
description = DescriptionOrganisationSerializer(many=False, required=False)
class Meta:
model = Organisation
fields = '__all__'
def create(self, validated_data):
error_msg = 'Save error'
users_data = validated_data.pop('users')
contacts_data = validated_data.pop('contacts')
description_data = validated_data.pop('description')
organisation = Organisation.objects.create(**validated_data)
try:
for user_data in users_data:
Users.objects.create(organisation=organisation, **user_data)
for contact_data in contacts_data:
Contacts.objects.create(organisation=organisation, **contact_data)
DescriptionOrganisation.objects.create(organisation=organisation, **description_data)
except:
organisation.delete()
raise serializers.ValidationError(error_msg)
return {}
def update(self, instance, validated_data):
pass
When I save, everything goes well. But when I try to update, the serializer fails validation. The error text in the comments.
"""
Класс для работы с данными для супер админа
"""
queryset = Organisation.objects.all()
serializer_class = OrganisationSuperAdminSerializer
permission_classes = [permissions.AllowAny, ]
def update(self, request, pk=None, *args, **kwargs):
serializer: serializers.ModelSerializer = self.get_serializer(self.get_object(), data=request.data)
print(serializer.is_valid()) # False
print(serializer.errors) # {'users': [{'email': [ErrorDetail(string='email must be unique', code='unique')]}], 'description': {'non_field_errors': [ErrorDetail(string='The fields inn, kpp must make a unique set.', code='unique')]}}
return response.Response(status=200)
I don't want to disable validation of unique fields. But I can't find information how to validate through the serializer update.
Other serializers:
class UsersSerializer(serializers.ModelSerializer):
email = serializers.CharField(max_length=128,
validators=[validators.UniqueValidator(
queryset=Users.objects.all(),
message='email must be unique'
)]
)
class Meta:
model = Users
fields = '__all__'
class DescriptionOrganisationSerializer(serializers.ModelSerializer):
organisation = serializers.PrimaryKeyRelatedField(required=False, queryset=DescriptionOrganisation.objects.all())
class Meta:
model = DescriptionOrganisation
fields = '__all__'
class ContactsSerializer(serializers.ModelSerializer):
organisation = serializers.PrimaryKeyRelatedField(required=False, queryset=Contacts.objects.all())
class Meta:
model = Contacts
fields = '__all__'
I am building a notification system for a company, where admin users can create Projects and add users to them. The Project model has 9 attributes but I only want to show 3 or 4 fields when a Project is created, but show them all when an existing Project is updated.
This change will only need to be reflected on the Django admin site, so I have extended the ProjectAdmin with my own ProjectForm, where I extend the init method to check if it is a new instance and if so remove certain fields.
# models.py
class Project(models.Model):
project_number = models.IntegerField()
name = models.CharField(max_length=100)
permit = models.CharField(max_length=100, blank=True, default='')
is_active = models.BooleanField(default=True)
users = models.ManyToManyField(CustomUser, blank=True, related_name='project_users')
# add a default
levels = models.ManyToManyField('Level', blank=True, related_name='project_levels')
total_contract_hours = models.IntegerField(default=0, blank=True, verbose_name='Total Design Hours')
hours_used = models.IntegerField(default=0, blank=True, verbose_name='Total Design Hours Used')
notes = models.ManyToManyField('notes.ProjectNote', related_name='core_project_notes', blank=True)
history = HistoricalRecords()
def __str__(self):
ret_str = "{} {}".format(self.project_number, self.name)
if self.permit:
ret_str += " | Permit: {}".format(self.permit)
return ret_str
# admin.py
class ProjectForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ProjectForm, self).__init__(*args, **kwargs)
attrs = {'class': 'form-control', 'required': True}
if self.instance and self.instance.pk is None:
# creating project
exclude = ['is_active', 'users', 'levels', 'hours_used', 'notes']
for field in exclude:
try:
del self.fields[field]
except ValueError:
print('{} does not exist'.format(field))
for field in self.fields.values():
field.widget.attrs = attrs
class Meta:
model = Project
fields = ['project_number', 'name', 'total_contract_hours']
class ProjectAdmin(admin.ModelAdmin):
form = ProjectForm
fields = ['project_number', 'name', 'permit', 'is_active', 'users', 'levels', 'total_contract_hours', 'hours_used', 'notes']
As I stated I only want basic Project fields on creation, but show all attributed when updating existing Project. With just these changes, I now get a KeyError:
KeyError: "Key 'is_active' not found in 'ProjectForm'. Choices are:
name, permit, project_number, total_contract_hours."
However, when I print the available fields it returns an OrderedDict with all of the model attributes as keys. What am I doing wrong? Thanks!
I figured it out, the field must be in listed in Meta and then you just set the field to be a hidden field.
class ProjectForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ProjectForm, self).__init__(*args, **kwargs)
print("Adding project")
if not self.instance or self.instance.pk is None:
for name, field in self.fields.items():
if name in ['design_manager', ]:
field.widget = forms.HiddenInput()
class Meta:
model = Project
fields = ['project_number', 'name', 'design_manager', 'total_contract_hours']
class ProjectAdmin(admin.ModelAdmin):
form = ProjectForm
def save_model(self, request, obj, form, change):
obj.design_manager = request.user
super().save_model(request, obj, form, change)
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.
I have a model and viewset related to this model,
here is my code :
class EMAILTemplate(models.Model):
""" Message SMS Template """
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=255)
body = models.TextField()
tokens = models.TextField()
created = models.DateTimeField(auto_now_add=True)
def save(self, *args, **kwargs):
self.tokens = ",".join(re.findall(r'{{\s*(.*?)\s*}}', self.body))
super().save(*args, **kwargs)
I don't want field tokens to be in my create form in Django rest framework create or edit form, because as you see It's going to be extracted from body field.
but I want to have this field in view single model or list of models.
and here is my ModelSerializer :
class EmailTemplateSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = EMAILTemplate
fields = ('name', 'body', 'user', 'tokens')
You can specify tokens as read only field:
class EmailTemplateSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = EMAILTemplate
fields = ('name', 'body', 'user', 'tokens')
read_only_fields = ('tokens',)
You can overwrite the current to_representation method
class EmailTemplateSerializer(serializers.HyperlinkedModelSerializer):
def to_representation(self, obj):
try:
if self.context['view'].action in ['list', 'detail']:
# get the original representation
ret = super(serializers.HyperlinkedModelSerializer, self).to_representation(obj)
# remove 'tokens' field
ret.pop('tokens')
return ret
except KeyError:
return super(serializers.HyperlinkedModelSerializer, self).to_representation(obj)
return super(serializers.HyperlinkedModelSerializer, self).to_representation(obj)
Try to overwrite User models by the following code, but somehow I cannot overwrite the max_length and min_length of username.
More specifically, when I check by python manage.py shell, I do overwrite them. But it seems has no effect on the html which was rendered(username maxlength is still 150).
Don't know which parts get wrong, please help.
from django import forms
from django.contrib.auth.models import User
class RegistrationForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(RegistrationForm, self).__init__(*args, **kwargs)
email = self.fields['email']
username = self.fields['username']
email.required = True
email.label_suffix = ' '
username.label_suffix = ' '
######### this is not work!!!###############
username.min_length = 6
username.max_length = 30
############################################
class Meta:
model = User
fields = ('username', 'email')
labels = {
'username': '帳號',
}
help_texts = {
'username': '',
}
Instead of modifying the form, you should modify/override the model.
I recommend using django-auth-tools for building your own custom user model. It supplies basic models, views and forms which can be easily extended.
If you are trying to override just the model form field, you could do something like this
class RegistrationForm(forms.ModelForm):
username = forms.CharField(required=True, min_length=6, max_length=30)
class Meta:
model = User
fields = ('username', 'email')
or
class RegistrationForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(RegistrationForm, self).__init__(*args, **kwargs)
self.fields['username'] = forms.CharField(required=True, min_length=6, max_length=30)
class Meta:
model = User
fields = ('username', 'email')
But I would recommend creating a Custom User Model inherited from AbstractBaseUser to override the username or email field as documented in https://docs.djangoproject.com/en/1.10/topics/auth/customizing/