I am new to Django. I am using Django 1.8 and Python 3.4.
class Card(models.Model):
STATUS_EXPIRED = "EX"
STATUS_ACTIVE = "AC"
STATUS_DEACTIVATED = "DE"
STATUS_CHOICES = (
(STATUS_ACTIVE, "Active"),
(STATUS_EXPIRED, "Expired"),
(STATUS_DEACTIVATED, "Deactivated")
)
id = models.AutoField(primary_key = True)
series = models.CharField(verbose_name="Series", max_length=8, null=False, blank=False)
number = models.CharField(verbose_name="Number", max_length=16, null=False, blank=False)
issue_date = models.DateTimeField(verbose_name="Issue Date", auto_now=True, null=False, blank=False)
expire_date = models.DateTimeField(verbose_name="Expiry Date", auto_now=False, null=False, blank=False)
status = models.CharField(verbose_name="Status", max_length=3, null=False, blank=False, default="AC")
How do I ensure that expire_date is never less than issue_date in the database? How do I enforce this condition in Django-admin interface when creating objects of Card class?
You can do a form validation in admin like this;
from models import Card
from django.contrib import admin
from django import forms
class CardForm(forms.ModelForm):
class Meta:
model = Card
fields = ('series', 'number', 'issue_date', 'expire_date', 'status')
def clean(self):
issue_date = self.cleaned_data.get('issue_date')
expire_date = self.cleaned_data.get('expire_date')
if expire_date < issue_date:
raise forms.ValidationError("Wrong dates")
return self.cleaned_data
class CardAdmin(admin.ModelAdmin):
form = CardForm
list_display = ('series', 'number', 'issue_date', 'expire_date', 'status')
admin.site.register(Card, CardAdmin)
You can use a custom model form ( Model forms ) and validate the data before saving and while registering do something like
class TestAdmin(admin.ModelAdmin):
form = ModelForm
list_display = ('field1', 'field2')
admin.site.register(Lecture, LectureAdmin)
Related
serializers.py
class RegSerializer(serializers.ModelSerializer):
admin = serializers.SlugRelatedField(slug_field='username', read_only=True)
class Meta:
model = Registration
fields = [
'id', 'rooms', 'first_name', 'last_name','admin', 'pasport_serial_num', 'birth_date', 'img', 'visit_date',
'leave_date', 'guest_count', 'room_bool']
models.py
class Rooms(models.Model):
objects = None
room_num = models.IntegerField(verbose_name='Комната')
room_bool = models.BooleanField(default=True, verbose_name='Релевантность')
category = models.CharField(max_length=150, verbose_name='Категория')
price = models.IntegerField(verbose_name='Цена (сум)', null=True)
def __str__(self):
return f'{self.room_num}'
class Meta:
verbose_name = 'Комнату'
verbose_name_plural = 'Комнаты'
class Registration(models.Model):
objects = None
rooms = models.ForeignKey(Rooms, on_delete=models.CASCADE, verbose_name='Номер',
help_text='Номер в который хотите заселить гостя!',
)
first_name = models.CharField(max_length=150, verbose_name='Имя')
last_name = models.CharField(max_length=150, verbose_name='Фамилия')
admin = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='Администратор')
pasport_serial_num = models.CharField(max_length=100, verbose_name='Серия паспорта', help_text='*AB-0123456')
birth_date = models.DateField(verbose_name='Дата рождения')
img = models.FileField(verbose_name='Фото документа', help_text='Загружайте файл в формате .pdf')
visit_date = models.DateField(
default=django.utils.timezone.localdate, verbose_name='Дата прибытия')
leave_date = models.DateField(blank=True, null=True, verbose_name='Дата отбытия', default='После ухода!')
guest_count = models.IntegerField(default=1, verbose_name='Кол-во людей')
room_bool = models.BooleanField(default=False, verbose_name='Релевантность',
help_text='При бронирование отключите галочку')
price = models.IntegerField(verbose_name='Цена (сум)', null=True)
def __str__(self):
return f'{self.rooms},{self.last_name},{self.first_name},{self.room_bool}'
class Meta:
verbose_name = 'Номер'
verbose_name_plural = 'Регистрация'
how can I make it so that the name of the user who registered room is indicated in the admin field and without the right to change only readonly?
can this be done at all?
thanks in advance for your reply
You can pass additional attributes to serilizer's save method. In your view, you can call serializer save() with admin argument like this:
def your_view(request):
# your code
serializer.save(admin=request.user)
Or if you want to do it on admin page, you can override your admin's save_model method. Also you should specify admin as a readonly:
class RegistrationAdmin(admin.ModelAdmin):
readonly_fields = ('admin',)
def save_model(self, request, obj, form, change):
if not obj.pk:
# Only set admin during the first save.
obj.admin = request.user
super().save_model(request, obj, form, change)
I set serializers.HiddenField and by default set CurrentUserDefault() from serializers
automatically substitutes the value of the current admin and at the same time the admin field now goes to the api
class RegSerializer(serializers.ModelSerializer):
admin = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = Registration
exclude = ['price', 'visit_date']
This is the model that I want to show on the admin panel. I'm registering the model via admin.py file with admin.site.register(Ad). I tried to re-write the register line twice, and an exception appeared that the model is already registered.
class Ad(AdModel):
plate = models.CharField(max_length=50, unique=True)
description = models.TextField(max_length=500)
ad_type = models.CharField(
max_length=255,
choices=AdTypes.get_choices(),
default=AdTypes.OFFERING,
)
price = models.PositiveIntegerField(
default=0,
help_text='In cents'
)
location = models.CharField(
max_length=255,
choices=AdLocations.get_choices(),
default=AdLocations.VILNIUS,
)
user = models.ForeignKey(User, on_delete=models.PROTECT)
approved_date = models.DateField(null=True, blank=True)
approved_by = models.ForeignKey(
User, on_delete=models.PROTECT, related_name='approved_by', null=True
)
The two base models:
class UUIDBaseModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
class Meta:
abstract = True
class AdModel(UUIDBaseModel):
expires_at = models.DateTimeField(null=True)
is_draft = models.BooleanField(default=False)
is_active = models.BooleanField(default=False)
class Meta:
abstract = True
This is really strange, maybe that could be the problem because of the naming 'Ad'? I have a serializer for this model and everything works just fine, but the admin panel doesn't want to display it.
views.py
class AdCreateViewSet(ModelViewSet, CreateModelMixin):
serializer_class = AdCreateSerializer
permission_classes = (AllowAny,)
filter_backends = [DjangoFilterBackend]
search_fields = ('plate', 'description', 'user__email')
queryset = Ad.objects.select_related('user')
def perform_create(self, serializer):
user = User.objects.first()
serializer.save(user=user) # self.request.user)
serializers.py
class AdCreateSerializer(CustomAdSerializer):
class Meta:
model = Ad
exclude = ['expires_at']
read_only_fields = ('user',)
I'm working on a project using Python(3.7) and Django(2.2) in which I have implemented my models for multiple user types with custom user model as the base model. Everything working fine except the admin side, I have register these modles to admin but when I try to add an object from admin interface it's giving an error.
Here's what I have tried so far:
From models.py:
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=254, unique=True)
title = models.CharField(max_length=255, blank=False)
user_type = models.CharField(max_length=255, choices=USER_TYPE, blank=False)
gender = models.CharField(max_length=255, choices=CHOICES, blank=False)
contenst = models.CharField(max_length=255, blank=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
last_login = models.DateTimeField(null=True, blank=True)
date_joined = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'email'
EMAIL_FIELD = 'email'
REQUIRED_FIELDS = ['password']
objects = UserManager()
def get_absolute_url(self):
return "/users/%i/" % (self.pk)
class PersonalBelow18(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
dob = models.DateField(blank=False)
customer_id = models.BigIntegerField(blank=False)
collection_use_personal_data = models.BooleanField(blank=False)
reference_identities = models.ForeignKey(Identities, blank=False, on_delete=models.CASCADE, related_name='refs')
def __str__(self):
return self.user.email+'\'s account with ' + str(self.customer_id)
class PersonalAbove18(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
dob = models.DateField(blank=False)
customer_id = models.BigIntegerField(blank=False)
contact_email = models.EmailField(blank=False)
reference_identities = models.ForeignKey(Identities, blank=False, on_delete=models.CASCADE)
contact_no = PhoneNumberField(blank=True, help_text='Phone number must be entered in the'
'format: \'+999999999\'. Up to 15 digits allowed.')
collection_use_personal_data = models.BooleanField(blank=False)
def __str__(self):
return self.user.email+'\'s account with ' + str(self.customer_id)
class Parent(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
contact_email = models.EmailField(blank=False)
customer_id = models.BigIntegerField(blank=True)
contact_no = PhoneNumberField(blank=True, help_text='Phone number must be entered in the'
'format: \'+999999999\'. Up to 15 digits allowed.')
collection_use_personal_data = models.BooleanField(blank=False)
class GroupContactPerson(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
contact_email = models.EmailField(blank=False)
customer_id = models.BigIntegerField(blank=False)
contact_no = PhoneNumberField(blank=True, help_text='Phone number must be entered in the'
'format: \'+999999999\'. Up to 15 digits allowed.')
department = models.CharField(max_length=255, blank=False)
address = models.TextField(max_length=255, blank=False)
and here's how I register these models to admin:
From admin.py:
class UserAdmin(BaseUserAdmin):
fieldsets = (
(None, {'fields': ('email', 'password', 'title', 'user_type',
'gender', 'contenst', 'last_login')}),
('Permissions', {'fields': (
'is_active',
'is_staff',
'is_superuser',
'groups',
'user_permissions',
)}),
)
add_fieldsets = (
(
None,
{
'classes': ('wide',),
'fields': ('email', 'password1', 'password2')
}
),
)
list_display = ('email', 'title', 'is_staff', 'last_login')
list_filter = ('is_staff', 'is_superuser', 'is_active', 'groups')
search_fields = ('email',)
ordering = ('email',)
filter_horizontal = ('groups', 'user_permissions',)
admin.site.register(User, UserAdmin)
admin.site.register(PersonalBelow18)
admin.site.register(PersonalAbove18)
admin.site.register(Parent)
admin.site.register(GroupContactPerson)
The Parent and GroupContactPerson models are working well on admin side but the when I try to add an object for PersonalBelow18 & PersonalAbove18 models, it gives the following error as:
TypeError: str returned non-string (type int)
Here's how I debug this problem in these models:
I start removing all fields one-by-one
Remove a field from model & form and perform migrations
Then test the admin
Then I found that when I removed the reference_identities field it start working, so I get that this model was returning an integer, so I fixed that model and it fix the issue.
In short, it's a good approach to find a path to the actual place of problem by removing fields one-by-one and test the admin.
Scenario:
I instantiate a ModelForm and pass it to a template which displays the form. When POST is submitted, code tries to search the database by any of the given inputs. I dont require all inputs to be entered as in the Model. I just need one (or more, if user desires to do an AND search) to be entered.
Question: How can I make any of the ModelForm fields optional, where in the Model, the field isnt optional. The field isnt optional in the Model because I have another ModelForm based on the same Model, where user is required to enter all his details.
My model:
class customer(models.Model):
# Need autoincrement, unique and primary
cstid = models.AutoField(primary_key=True, unique=True)
name = models.CharField(max_length=35)
age=models.IntegerField()
gender_choices = (('male', 'Male'),
('female', 'Female'))
gender = models.CharField(
choices=gender_choices, max_length=10, default='male')
maritalstatus_choices = ( ('married', 'Married'),
('unmarried', 'Unmarried'))
maritalstatus = models.CharField(
choices=maritalstatus_choices, max_length=10, default='Unmarried')
mobile = models.CharField(max_length=15, default='')
alternate = models.CharField(max_length=15, default='')
email = models.CharField(max_length=50, default='', blank=True)
address = models.CharField(max_length=80, default='', blank=True)
city = models.CharField(max_length=25, default='', blank=True)
occupation = models.CharField(max_length=25, default='', blank=True)
bloodgroup_choices = (('apos', 'A+'),
('aneg', 'A-'),
('bpos', 'B+'),
('bneg', 'B-'),
('opos', 'O+'),
('oneg', 'O-'),
('abpos', 'AB+'),
('abneg', 'AB-'),
('unspecified', '-')
)
bloodgroup = models.CharField(choices=bloodgroup_choices, max_length=3, default='-', blank=True)
class Meta:
unique_together = ["name", "mobile", "age"]
def __str__(self):
return self.name
My form:
class CheckinPatientMetaForm(ModelForm):
class Meta:
model = customer
exclude = [
'gender',
'maritalstatus',
'occupation',
'bloodgroup'
]
views.py:
def checkin_patient(request):
results = ''
if request.method == 'POST':
form = CheckinPatientMetaForm(request.POST)
print("POST data",request.POST)
else:
form = CheckinPatientMetaForm()
return render(request, 'clinic/checkin.html', {'rnd_num': randomnumber(), 'form': form, 'SearchResults': results})
As #bdbd mentioned in comments, you can do it by specifying by required=False.
For example, if you want to age field to be optional, add it explicitly as
from django import forms
class CheckinPatientMetaForm(ModelForm):
age = forms.IntegerField(required=False)
class Meta:
model = customer
exclude = [
'gender',
'maritalstatus',
'occupation',
'bloodgroup'
]
How do I go about grouping a set of fields within the Django Admin. By this I mean
Table 1: User - User Key - User Name
Table 2: Post - Post Key - User Key (FK)
In the PostAdmin I would like to display the User Name of the Author also perform an action on the posts. This works but displays the user name for each post created by the user.
Is there a way I could just display the user name once but in the action update all posts created by the user? - Update all posts with is_sealed = true.
Model
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_("email address"), unique=True)
first_name = models.CharField(
_("first name"), max_length=50,
validators=[RegexValidator(
regex_alpha, code='invalid_first_name',
message=regex_alpha_message,
)]
)
middle_name = models.CharField(
_("middle name"), max_length=50, blank=True,
null=True, default=None,
validators=[RegexValidator(
regex_alpha, code='invalid_middle_name',
message=regex_alpha_message,
)]
)
class Post(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL)
title = models.CharField(_('title'), max_length=250)
label = models.ForeignKey(Label, verbose_name=_('label'))
significance = models.CharField(max_length=3, choices=SIGNIFICANCE_CHOICES)
is_sealed = models.BooleanField(_('Is Sealed?'), default=False)
event_date = models.DateField()
message = models.TextField(_("Message"), blank=True, default='')
PostAdmin (I need to fix the issue here)
class PostAdmin(admin.ModelAdmin):
list_display = ['owner','is_sealed']
ordering = ['owner']
actions = [open_vault]
search_fields = ['owner__email',]
list_filter = ['owner__email']
So, according to your comments, you must write a custom method inside UserAdmin for displaying posts related to a user/author, like this:
from django.utils.html import mark_safe
class UserAdmin(admin.ModelAdmin):
list_display = ['email', 'get_posts']
# ... other settings here
def get_posts(self, obj):
to_return = '<ul>'
for post in obj.post_set.all():
to_return += '<li>{}</li>'.format(post.title)
to_return += '</ul>'
return mark_safe(to_return)