django check-contraint boolean expression [postgresql] - django

So I have a model like this
class Role(BaseModel):
class Meta:
verbose_name = 'role'
verbose_name_plural = 'roles'
ordering = ['position', 'cluster']
required_db_features = {
'supports_deferrable_unique_constraints',
}
constraints = [
models.UniqueConstraint(
fields=['position', 'cluster'],
name='deferrable_unique_role_position',
deferrable=models.Deferrable.DEFERRED
),
models.CheckConstraint(
name='default_role_check',
check=models.Q(
is_default=True,
position=1,
color='#969696',
name='#everyone'
)
)
]
permission_flags = [
'READ_DATA', 'WRITE_DATA', 'MANAGE_RECORDS', 'MANAGE_ROLES',
'MANAGE_CLUSTER', 'MANAGE_DATASHEETS', 'MANAGE_FIELDS', 'MANAGE_CONSTRAINTS',
'KICK_MEMBERS', 'MANAGE_MEMBERS',
]
default_perm_flags = ['READ_DATA', 'WRITE_DATA', 'MANAGE_RECORDS']
def __str__(self):
return self.name
objects = managers.RolesManager()
positions = managers.PositionalManager()
permissions = BitField(flags=permission_flags, default=default_perm_flags, db_index=True)
position = models.PositiveSmallIntegerField(null=True, blank=True, db_index=True, editable=True)
name = models.CharField(max_length=100, validators=[MinLengthValidator(2)], db_index=True, default='new role')
color = ColorField(db_index=True, default='#969696')
is_default = models.BooleanField(default=False, db_index=True)
cluster = models.ForeignKey('api_backend.Cluster', on_delete=models.CASCADE, editable=False)
Basically I want to enforce a check constraint on this model such that
when the model has the is_default field set to True,
the name must always be #everyone
the color must always be #969696
the position must always be 1.
I tried to implement the same, however my current implementation doesn't work.
While using serializers, I can edit the default role's name, and no exceptions are raised.
I am using postgresql at the backend.
Can someone please help me?
thanks in advance!

Adding an is_default=False case get you closer to your conditions.
I tested with Django==3.1.4 and the OR method resulted in this constraint:
"default_role_check" CHECK (color::text = '#969696'::text AND is_default AND name::text = '#everyone'::text AND "position" = 1 OR NOT is_default)
Constraint example:
class Role(BaseModel):
class Meta:
verbose_name = 'role'
verbose_name_plural = 'roles'
ordering = ['position', 'cluster']
required_db_features = {
'supports_deferrable_unique_constraints',
}
constraints = [
models.UniqueConstraint(
fields=['position', 'cluster'],
name='deferrable_unique_role_position',
deferrable=models.Deferrable.DEFERRED
),
models.CheckConstraint(
name='default_role_check',
check=(models.Q(
is_default=True,
position=1,
color='#969696',
name='#everyone'
) |
models.Q(
is_default=False,
)
)
)
]
permission_flags = [
'READ_DATA', 'WRITE_DATA', 'MANAGE_RECORDS', 'MANAGE_ROLES',
'MANAGE_CLUSTER', 'MANAGE_DATASHEETS', 'MANAGE_FIELDS', 'MANAGE_CONSTRAINTS',
'KICK_MEMBERS', 'MANAGE_MEMBERS',
]
default_perm_flags = ['READ_DATA', 'WRITE_DATA', 'MANAGE_RECORDS']
def __str__(self):
return self.name
objects = managers.RolesManager()
positions = managers.PositionalManager()
permissions = BitField(flags=permission_flags, default=default_perm_flags, db_index=True)
position = models.PositiveSmallIntegerField(null=True, blank=True, db_index=True, editable=True)
name = models.CharField(max_length=100, validators=[MinLengthValidator(2)], db_index=True, default='new role')
color = ColorField(db_index=True, default='#969696')
is_default = models.BooleanField(default=False, db_index=True)
cluster = models.ForeignKey('api_backend.Cluster', on_delete=models.CASCADE, editable=False)

Related

Django rest m2m nested serializer create/update nested objects

I have a model Scenario:
class Scenario(models.Model):
stakeholder = models.ForeignKey(User, related_name='scenarios', blank=True,)
tasks = models.ManyToManyField(Task, blank=True)
Task objects are already created and from my fronted I create a scenario by adding a few of the existing tasks to it.
Serializer:
class ScenarioSerializer(serializers.ModelSerializer):
tasks = TaskSerializer(many=True, required=False)
class Meta:
model = Scenario
fields = '__all__'
def create(self, validated_data):
tasks = validated_data.pop('tasks')
scenario = Scenario.objects.create(**validated_data)
for task in tasks:
Task.objects.update(scenario=scenario, **task)
return scenario
Without create() method it throws error:
AssertionError: The `.create()` method does not support writable nested fields by default.
Write an explicit `.create()` method for serializer `scenarios.serializers.ScenarioSerializer`, or set `read_only=True` on nested serializer fields.
When I added this method, error was gone but it threw:
FieldError: Cannot update model field <ManyToManyRel: scenarios.scenario> (only non-relations and foreign keys permitted)
What am I doing wrong? I do not want to create new tasks, just create a new scenario and attach existing tasks to it.
Update: Task model:
class Task(models.Model):
stakeholder = models.ForeignKey(User, related_name='tasks', blank=True, )
project = models.ForeignKey(Project, related_name='project_tasks' )
title = models.CharField(max_length=50, blank=True, null = True, )
OFTEN_CHOICES = (
('DS', 'Daily several times'),
('DO', 'Daily once'),
('WO', 'Weekly once'),
('MO', 'Monthly once'),
('YO', 'Yearly once'),
)
how_often = models.CharField(blank=True, null = True, choices=OFTEN_CHOICES, max_length=2, )
IMPORTANCE_CHOICES = (
('EI', 'Extremely important'),
('RI', 'Rather important'),
('LI', 'Less important'),
)
how_important_task = models.CharField(blank=True, null = True, choices=IMPORTANCE_CHOICES, max_length=2, )
role = models.CharField(max_length=20, blank=True, null = True, )
why_perform_task = models.CharField(max_length=150, blank=True, null = True, )
why_important_task = models.CharField(max_length=150, blank=True, null = True, )
sequence_of_actions = models.CharField(max_length=500, blank=True, null = True, )
tools_used = models.CharField(max_length=150, blank=True, null = True, )
special_training_required = models.NullBooleanField(blank=True, null = True, default=False, )
what_training_required = models.CharField(max_length=150, blank=True, null = True, )
what_can_go_wrong = models.CharField(max_length=150, blank=True, null = True, )
effects_of_task = models.CharField(max_length=150, blank=True, null = True, )
special_vocabulary_used = models.CharField(max_length=150, blank=True, null = True, )
people_involved = models.CharField(max_length=150, blank=True, null = True, )
any_improvements = models.CharField(max_length=150, blank=True, null = True, )
how_important_improvement = models.CharField(blank=True, null = True, choices=IMPORTANCE_CHOICES, max_length=2, )
benefits_of_improvement = models.CharField(max_length=150, blank=True, null = True, )
sample request data:
{"tasks":[{"id":7,"title":"Seven","how_often":"","how_important_task":"","role":"","why_perform_task":"","why_important_task":null,"sequence_of_actions":"","tools_used":"","special_training_required":null,"what_training_required":"","what_can_go_wrong":"","effects_of_task":"","special_vocabulary_used":"","people_involved":"","any_improvements":"","how_important_improvement":"","benefits_of_improvement":"","stakeholder":2,"project":1}]}
you done first part and now need add method update here docs:
for example
class ScenarioSerializer(serializers.ModelSerializer):
tasks = TaskSerializer(many=True, required=False)
class Meta:
model = Scenario
fields = '__all__'
def create(self, validated_data):
tasks = validated_data.pop('tasks', [])
instance = Scenario.objects.create(**validated_data)
for task_data in tasks:
task = Task.objects.get(pk=task_data.get('id'))
instance.tasks.add(task)
return instance
def update(self, instance, validated_data):
tasks = validated_data.pop('tasks', [])
instance = super().update(instance, validated_data)
for task_data in tasks:
task = Task.objects.get(pk=task_data.get('id'))
instance.tasks.add(task)
return instance
This problem was solved by following the steps in this post https://lynxbee.com/solved-the-create-method-does-not-support-writable-nested-fields-by-default/.
pip install drf_writable_nested
from drf_writable_nested import WritableNestedModelSerializer
class YourSerializer(WritableNestedModelSerializer, serializers.ModelSerializer):
pass

Django ManyToManyField inline list values

I have two Django models related by a ManyToManyField relationship. Everything works fine except for the inline add dropdown which lists ugly automatically created object names instead of allowing me to format it. How can I specify that?
Models:
class Job(models.Model):
type = models.CharField(max_length=32, choices=JobChoices)
guid = models.CharField(max_length=32)
title = models.CharField(max_length=256)
started_time = models.DateTimeField()
ended_time = models.DateTimeField(blank=True, null=True)
enabled = models.BooleanField(default=False)
running = models.BooleanField(default=False)
working_job_status = models.CharField(max_length=32, choices=StatusCoices)
working_job_length = models.IntegerField(blank=True, null=True)
working_job_progress = models.IntegerField(blank=True, null=True)
working_job_eta_sec = models.IntegerField(blank=True, null=True)
RepeatUnit = (
('s', 'Second'),
('m', 'Minute'),
('h', 'Hour'),
('d', 'Day'),
('W', 'Week'),
('M', 'Month'),
('Y', 'Year'),
)
class Schedule(models.Model):
title = models.CharField(max_length=128)
job = models.ManyToManyField(Job, blank=True, null=True)
start_time = models.DateTimeField(null=False)
end_time = models.DateTimeField(blank=True, null=True)
repeat_unit = models.CharField(blank=True, null=True, max_length=1, choices=RepeatUnit)
repeat_every = models.IntegerField(blank=True, null=True)
repeat_max_count = models.IntegerField(blank=True, null=True)
def __unicode__(self):
return f'{self.title}'
Admin:
class ScheduleAdmin(admin.ModelAdmin):
list_display = ['id', 'title', 'start_time', 'end_time', 'repeat_unit', 'repeat_every', 'repeat_max_count']
class ScheduleInline(admin.TabularInline):
model = Schedule.job.through
min_num = 0
extra = 0
# fields = ('title', )
verbose_name = "Schedule"
verbose_name_plural = "Schedules"
class JobAdmin(admin.ModelAdmin):
list_display = ['id', 'type', 'guid', 'title', 'started_time', 'ended_time', 'enabled', 'running', 'progress']
inlines = [ScheduleInline,]
admin.site.register(Schedule, ScheduleAdmin)
admin.site.register(Job, JobAdmin)
And, when I click on the inlines drop-down menu I get:
changing from __unicode__(self) to __str__(self) did the trick

Django rest framework - cant serialize query set

I try to serialize query set
def do(self):
reservations = Reservation.objects.all()
serializer = ReservationSerializer(data=reservations, many=True)
if serializer.is_valid():
encoded_data = json.dumps(serializer.data)
r = requests.post('http://httpbin.org/post', headers={'Content-Type': 'application/json'}, data=encoded_data)
print(r.text)
else:
print(serializer.errors)
And I always get error of
{u'non_field_errors': [u'Expected a list of items but got type "QuerySet".']}
I tried to use values() on query set, and then convert to list, but this way I get objects without nested models
model
class Reservation(models.Model):
start = models.DateField(verbose_name='Заезд', auto_now=False, auto_now_add=False, blank=False)
end = models.DateField(verbose_name='Выезд', auto_now=False, auto_now_add=False, blank=False)
check_in_time = models.TimeField(verbose_name='Время заезда', blank=False)
check_out_time = models.TimeField(verbose_name='Время выезда', blank=False)
has_refund = models.BooleanField(verbose_name='Возвратная бронь', default=True)
payed = models.BooleanField(verbose_name='Оплачено', default=False)
reserved_days = models.ManyToManyField(Day, blank=False)
additional_services = models.ManyToManyField(AdditionalService)
guest_name = models.CharField(verbose_name='Имя гостя', max_length=200, blank=True)
reservation_number = models.CharField(verbose_name='Номер брони', max_length=200, blank=True)
class AdditionalService(models.Model):
payment_date = models.CharField(verbose_name='Дата оплаты', max_length=200, blank=True)
payment_type = models.CharField(verbose_name='Форма оплаты', max_length=200, blank=False)
service = models.CharField(verbose_name='Услуга', max_length=200, blank=False)
quantity = models.IntegerField(blank=False)
price = models.FloatField(blank=False)
class Day(models.Model):
date = models.DateField(auto_now=False, auto_now_add=False)
price = models.FloatField()
payment_method = models.CharField(max_length = 200, blank=True)
payment_date = models.CharField(max_length=200, blank=True)
room = models.ForeignKey(Room, null=True, blank=True, verbose_name='Номер', on_delete=models.CASCADE)
class Room(models.Model):
name = models.CharField(max_length = 200, null=True)
id = models.AutoField(primary_key=True)
room_id = models.CharField(max_length = 200, null=False)
def __unicode__(self):
return self.name
serializers
class ReservationSerializer(serializers.ModelSerializer):
reserved_days = DaySerializer(many=True)
additional_services = AdditionalServicesSerializer(many=True)
class Meta:
model = Reservation
fields = [
'start',
'end',
'check_in_time',
'check_out_time',
'reserved_days',
'additional_services',
'has_refund',
'payed',
'guest_name',
'reservation_number',
]
class DaySerializer(serializers.ModelSerializer):
room = RoomSerializer()
class Meta:
model = Day
fields = [
'date',
'price',
'payment_method',
'payment_date',
'room',
]
class AdditionalServicesSerializer(serializers.ModelSerializer):
class Meta:
model = AdditionalService
fields = [
'payment_date',
'payment_type',
'service',
'quantity',
'price',
]
class RoomSerializer(serializers.ModelSerializer):
class Meta:
model = Room
fields = [
'room_id',
]
For serialization you don't need to use data keyword, just pass queryset as first positional argument:
serializer = ReservationSerializer(reservations, many=True)
return serializer.data

How to serialize list of strings with Django Rest Framework

I have serializer in Django rest framework as follows:
class StateSerializer(serializers.ModelSerializer):
kilometers = Field(source='mileage')
pictures = StatePictureSerializer(many=True, read_only=True)
class Meta:
model = Inspection # Options
fields = ('kilometers', 'inspection_date', 'pictures')
And StatePictureSerializer is as follows:
class StatePictureSerializer(serializers.ModelSerializer):
blob_url = Field(source='public_url')
class Meta:
model = Inspection_Picture
fields = ('blob_url', )
As result I get something as follows:
{
"kilometers": 64431,
"inspection_date": null,
"pictures": [
{"blob_url": "path/to/photo"},
{"blob_url": "path/to/photo"},
{"blob_url": "path/to/photo"},
{"blob_url": "path/to/photo"},
{"blob_url": "path/to/photo"}
]
}
Thus, pictures is an array of objects.
What I want is an array of strings, for example:
"pictures": ["path/to/photo", "path/to/photo", "path/to/photo", "path/to/photo", "path/to/photo"]
Any idea how to do that?
EDIT
Inspection model is as follows:
class Inspection(models.Model):
customerReference = models.CharField(max_length=50, blank=True, null=True)
extraReference = models.CharField(max_length=50, blank=True, null=True)
itemReference = models.IntegerField(blank=True, null=True)
vehicle = models.ForeignKey(to=Vehicle)
mileage = models.IntegerField()
timeStamp = models.DateTimeField(auto_now_add=True)
inspection_date = models.DateTimeField(null=True)
features = models.ManyToManyField(to=Feature)
pictures = models.ManyToManyField(to=Images, through="Inspection_Picture")
damages = models.ManyToManyField(to=Damage)
parts = models.ManyToManyField(to=Part)
checks = models.ManyToManyField(to=CheckType, through=Inspection_Check)
featuresFlat = models.ManyToManyField(to=FeatureFlat, through=Inspection_FeatureFlat)
And Images model is as follows:
class Images(models.Model):
"""Model for storing uploaded photos"""
filename = models.CharField(max_length=255)
extension = models.CharField(max_length=40)
key_data = models.CharField(max_length=90, unique=True, blank=True, null=True)
upload_date = models.DateTimeField(auto_now_add=True)
upload_identification = models.CharField(max_length=50, blank=True, null=True)
url = models.CharField(max_length=1024, blank=True, null=True)
stored = models.BooleanField(default=False)
thumbnailed = models.BooleanField(default=False)
thumbnailed_treated = models.BooleanField(default=False)
protected = models.BooleanField(default=False)
source = models.CharField(max_length=50, blank=True, null=True)
#property
def key_generate(self):
"""returns a string based unique key with length 80 chars"""
while 1:
key = str(random.getrandbits(256))
try:
Images.objects.get(key=key)
except:
return key
def __unicode__(self):
return self.upload_identification
def public_url(self):
return settings.AZURE_URL_FULL + self.url
I think in your case SerializerMethodField would be a right choice as follows. There may be <field_name> mismatch in the code below. Please make it working according your model. I assume the field names based on your serializer above.
class StateSerializer(serializers.ModelSerializer):
kilometers = Field(source='mileage')
pictures = serializers.SerializerMethodField('get_pictures')
class Meta:
model = Inspection # Options
fields = ('kilometers', 'inspection_date', 'pictures')
def get_pictures(self, obj):
return [each.public_url() for each in obj.pictures.all() ]

Wagtail - Foreignkey to ClusterableModel

I have a Course model with Option as ClusterableModel and Staff. Staff is related to Course model and Staf can choose the Option. I've done it similar things before with normal django, but I can't seems to figure out how to do it with Wagtail. The Option is showing normally on Course's Page but its empty on Staff's Tab. Here is my model :
class Option(models.Model):
name = models.CharField(max_length=255)
desc = models.CharField(max_length=255, blank=True, null=True)
class Meta:
abstract = True
def __str__(self):
return self.name
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
self.tajuk = self.name.title()
content_panels = [
FieldPanel('name'),
FieldPanel('desc')
]
class OptionPage(Orderable, Option):
page = ParentalKey("Course", related_name='course_option')
class Staf(Orderable):
page = ParentalKey('course.Course', related_name='pcourse')
jxr = models.ForeignKey('jxr.Staf',
null=True,
blank=True,
on_delete=models.PROTECT,
related_name='+'
)
vegetarian = models.BooleanField(default=False)
attend = models.BooleanField(default=False)
reason = models.CharField(max_length=255, blank=True, null=True)
replacement = models.ForeignKey('jxr.Staf',
null=True,
blank=True,
on_delete=models.PROTECT,
related_name='+'
)
option = models.ForeignKey(OptionPage,blank=True, null=True)
class Meta:
verbose_name_plural = 'Staf'
panels = [
FieldPanel('jxr'),
FieldPanel('vegetarian'),
FieldPanel('option'),
FieldPanel('attend'),
FieldPanel('reason'),
FieldPanel('replacement'),
]
class Course(Page):
tags = ClusterTaggableManager(through=CourseTag, blank=True)
picture = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
State = models.ForeignKey(State, default=1,
related_name='+',
on_delete=models.PROTECT,
)
location = RichTextField()
start = models.DateTimeField("Start",default=timezone.now)
end = models.DateTimeField("End", default=timezone.now)
max_staf = models.PositiveIntegerField(default=1)
intro = StreamField(CourseBlock())
status = models.CharField(max_length=30, choices=STATUS_KURSUS)
search_fields = Page.search_fields + [
index.SearchField('title'),
index.SearchField('intro')]
parent_page_types =['course.CourseIndex']
subpage_types=[]
content_panels = [
FieldPanel('title', classname="full title"),
MultiFieldPanel([
InlinePanel('kat_kursus', label='Category'),
FieldPanel('tags'),
InlinePanel('course_option', label='Options')
]),
ImageChooserPanel('picture'),
FieldPanel('state'),
FieldPanel('location'),
FieldPanel('start'),
FieldPanel('end'),
FieldPanel('max_staf'),
StreamFieldPanel('intro'),
FieldPanel('status'),
]
staf_panel = [
InlinePanel('pkursus', label='Staf')
]
edit_handler = TabbedInterface([
ObjectList(content_panels, heading='Content'),
ObjectList(staf_panel, heading='Staf'),
ObjectList(Page.promote_panels, heading='Promote'),
ObjectList(Page.settings_panels, heading='Settings', classname="settings"),
])