How can I test if two users can reserve the same car simultaneously?
def test_if_two_users_can_reserve_the_same_car_simultaneously(self):
with patch.object(
timezone,
"now",
return_value=make_aware(
datetime.datetime.combine(
datetime.date.today() + datetime.timedelta(days=1), datetime.time(10, 30, 0)
),
timezone=pytz.timezone("UTC"),
),
):
self.client.login(username=self.user.username, password=self.PASSWORD)
url = reverse("booking-list")
data = {
"book_for": datetime.datetime.combine(
datetime.date.today() + datetime.timedelta(days=1), datetime.time(11, 30, 0)
),
}
response = self.client.post(url, data=data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
with patch.object(
timezone,
"now",
return_value=make_aware(
datetime.datetime.combine(
datetime.date.today() + datetime.timedelta(days=1), datetime.time(10, 30, 0)
),
timezone=pytz.timezone("UTC"),
),
):
self.client.login(
username=self.another_user.username, password=self.PASSWORD
)
url = reverse("booking-list")
data = {
"book_for": datetime.datetime.combine(
datetime.date.today() + datetime.timedelta(days=1), datetime.time(11, 30, 0)
),
}
response = self.client.post(url, data=data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
This is how I wrote it, but in the unit test, it runs line by line. So first one will create then move to second one but I want them both run at same time. (Please answer with an example)
This is its model:
class Booking(models.Model):
id = models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
user_location = models.PointField(help_text="user current location")
user_address = models.TextField(help_text="user current address")
start_location = models.PointField(
blank=True,
help_text="location where the user pick up the car from",
)
start_address = models.TextField(
blank=True,
default="",
help_text="address where the user pick up the car from",
)
end_location = models.PointField(
null=True,
blank=True,
help_text="desitination location that a user wants to drive to or driven by the driver",
)
end_address = models.TextField(
blank=True,
default="",
help_text="desitination address that a user wants to drive to or driven by the driver",
)
vehicle = models.ForeignKey(
Vehicle,
on_delete=models.PROTECT,
)
book_for = models.DateTimeField()
drop_off_datetime = models.DateTimeField(
null=True,
blank=True,
help_text="the drop off time for booking",
)
status = models.CharField(
max_length=25,
choices=BookingStatus.choices,
default=BookingStatus.OPEN,
)
created_on = models.DateTimeField(auto_now_add=True)
updated_on = models.DateTimeField(auto_now=True)
As you can see I used UUIDField.
def perform_create(self, serializer):
serializer.save(user=self.request.user)
constraints:
class Meta:
constraints = [
UniqueConstraint(
fields=["user"],
condition=Q(status=BookingStatus.ACTIVE),
name="unique_active_booking",
)
]
ordering = (
"-book_for",
"start_address",
)
enter code here
so I upgraded from django 3.1 to 3.2 and on two of my models when I make migrations it keeps forcing me to change the auto id to BigAutoField even though I have (and had) DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' in my settings file before I updated.
operations = [
migrations.AlterField(
model_name='device',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
)
What is strange is that it is only affecting a couple models, but the rest are fine and they all use AutoField as well.
I am not against using BigAutoField but the migration fails because of foreignkey constraints.
I have deleted the migrations in question and also scrubbed them from the applied migrations table in the database. How can I stop Django from forcing this migration? I am at a loss right now.
Here is my Device Model. As you can see I have not specifically set the primary key, which I have not done on any other model either and those are fine.
from django.db import models
from company.models import Company
from django.db.models.signals import pre_save, post_save
from main.models import uuid_pre_save_generator
from django.conf import settings
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
import json
from django.urls import reverse
from django.utils.html import escape
from django.core.validators import RegexValidator, FileExtensionValidator
class UploadedImage(models.Model):
uuid = models.CharField(max_length=7, blank=True, unique=True, verbose_name='Image ID')
image = models.ImageField(null=True, blank=True, upload_to="images/",
validators=[FileExtensionValidator(['jpg', 'png', 'jpeg'], 'Only .jpg files allowed')])
objects = models.Manager()
class Meta:
verbose_name = "Uploaded Image"
verbose_name_plural = "Uploaded Images"
def __str__(self):
return self.uuid
pre_save.connect(uuid_pre_save_generator, sender=UploadedImage)
def update_device_theme(sender, instance, *args, **kwargs):
related_devices = instance.devices.all()
if related_devices:
for d in related_devices:
d.save()
def alert_device_update(sender, instance, *args, **kwargs):
device = Device.objects.get(uuid=instance.uuid)
channel_layer = get_channel_layer()
group_name = 'connect_{}'.format(instance.uuid)
data = json.dumps({
'message': {
'type': 'device_update',
'text': '',
'data': {
'device': device.connect,
},
'sender': {
'type': 'server',
'uuid': '',
'first_name': '',
'last_name': '',
'company': '',
'initial': '',
'display_name': 'Server',
},
},
})
async_to_sync(channel_layer.group_send)(
group_name,
{
'type': 'chatroom.message',
'text': data
}
)
class DeviceDisplayTheme(models.Model):
uuid = models.CharField(max_length=7, blank=True, unique=True, verbose_name='Theme ID')
name = models.CharField(max_length=30, blank=False, null=False, verbose_name='Theme Name')
show_header = models.BooleanField(default=True, verbose_name='Show Header')
show_footer = models.BooleanField(default=True, verbose_name='Show Footer')
header_bg_color = models.CharField(max_length=30, blank=False, null=False,
verbose_name='Header Background Color',
default='rgba(255,255,255,.1)',
validators=[
RegexValidator(
regex=r"^^rgba[(](?:\s*0*(?:\d\d?(?:\.\d+)?(?:\s*%)?|"
r"\.\d+\s*%|100(?:\.0*)?\s*%|(?:1\d\d|2[0-4]\d|"
r"25[0-5])(?:\.\d+)?)\s*,){3}\s*0*(?:\.\d+|1(?:\.0*)?)\s*[)]$",
message='The header background color you have chosen is not formatted'
' correctly',
),
])
badge_bg_color = models.CharField(max_length=30, blank=False, null=False,
verbose_name='Device ID Badge Background Color',
default='rgba(255,255,255,.2)',
validators=[
RegexValidator(
regex=r"^^rgba[(](?:\s*0*(?:\d\d?(?:\.\d+)?(?:\s*%)?|"
r"\.\d+\s*%|100(?:\.0*)?\s*%|(?:1\d\d|2[0-4]\d|"
r"25[0-5])(?:\.\d+)?)\s*,){3}\s*0*(?:\.\d+|1(?:\.0*)?)\s*[)]$",
message='The badge background color you have chosen is not formatted'
' correctly',
),
])
bg_color = models.CharField(max_length=30, blank=False, null=False,
verbose_name='Background Color',
default='rgba(35,35,35,1)',
validators=[
RegexValidator(
regex=r"^^rgba[(](?:\s*0*(?:\d\d?(?:\.\d+)?(?:\s*%)?|"
r"\.\d+\s*%|100(?:\.0*)?\s*%|(?:1\d\d|2[0-4]\d|"
r"25[0-5])(?:\.\d+)?)\s*,){3}\s*0*(?:\.\d+|1(?:\.0*)?)\s*[)]$",
message='The background color you have chosen is not formatted correctly',
),
])
font_color = models.CharField(max_length=30, blank=False, null=False,
verbose_name='Font Color',
default='rgba(255,255,255,1)',
validators=[
RegexValidator(
regex=r"^^rgba[(](?:\s*0*(?:\d\d?(?:\.\d+)?(?:\s*%)?|"
r"\.\d+\s*%|100(?:\.0*)?\s*%|(?:1\d\d|2[0-4]\d|"
r"25[0-5])(?:\.\d+)?)\s*,){3}\s*0*(?:\.\d+|1(?:\.0*)?)\s*[)]$",
message='The font color you have chosen is not formatted correctly',
),
])
idle_text = models.CharField(max_length=50, blank=True, null=True,
verbose_name="Idle Text",
default='Press Help Button In Case of Emergency')
help_requested_text = models.CharField(max_length=50, blank=True, null=True,
verbose_name="Help Requested Text",
default='Emergency Help Requested')
active_text = models.CharField(max_length=50, blank=True, null=True,
verbose_name="Active Call Text",
default='Emergency Call Accepted')
response_prompt_text = models.CharField(max_length=50, blank=True, null=True,
verbose_name="Response Prompt Text",
default='Press Yes/No Buttons to Respond')
company = models.ForeignKey(Company, related_name='themes', verbose_name='Company',
blank=True, null=True, on_delete=models.CASCADE)
date_updated = models.DateTimeField(auto_now=True)
source_date_updated = models.DateTimeField(blank=True, null=True)
source_uuid = models.CharField(max_length=7, blank=True, null=True, verbose_name='Source ID')
objects = models.Manager()
class Meta:
verbose_name = "Device Display Theme"
verbose_name_plural = "Device Display Themes"
def __str__(self):
return self.name
#property
def settings(self):
idle_text = '' if not self.idle_text else self.idle_text
help_requested_text = '' if not self.help_requested_text else self.help_requested_text
active_text = '' if not self.active_text else self.active_text
response_prompt_text = '' if not self.response_prompt_text else self.response_prompt_text
return {
'pk': self.pk,
'name': escape(self.name),
'show_header': self.show_header,
'show_footer': self.show_footer,
'header_bg_color': self.header_bg_color,
'bg_color': self.bg_color,
'badge_bg_color': self.badge_bg_color,
'font_color': self.font_color,
'idle_text': escape(idle_text),
'help_requested_text': escape(help_requested_text),
'active_text': escape(active_text),
'response_prompt_text': escape(response_prompt_text),
}
#property
def settings_json(self):
return json.dumps(self.settings)
pre_save.connect(uuid_pre_save_generator, sender=DeviceDisplayTheme)
post_save.connect(update_device_theme, sender=DeviceDisplayTheme)
class Device(models.Model):
UUID_CREATED = 0
PROGRAMMED = 1
ASSIGNED = 2
lifecycle_stages = [
(UUID_CREATED, 'Unique ID Created'),
(PROGRAMMED, 'Memory Card Programmed'),
(ASSIGNED, 'Owner Assigned'),
]
statuses = [
('idle', 'Idle'),
('requested', 'Incoming Call Requested'),
('active', 'Live Call'),
]
uuid = models.CharField(max_length=6, blank=True, unique=True, verbose_name='Device ID')
# Call Status [ Idle, Requested, Active ]
state = models.CharField(max_length=10, choices=statuses, blank=False, default='idle', verbose_name="Call Status")
active = models.BooleanField(default=False, verbose_name='Active')
self_monitored = models.BooleanField(default=False, verbose_name='Self Monitored')
# Display Theme
theme = models.ForeignKey(DeviceDisplayTheme, related_name='devices', verbose_name='Display Theme',
blank=False, null=False, default=1, on_delete=models.SET_DEFAULT)
# Programming & Assignment
initialized = models.BooleanField(default=False, verbose_name='Initialized')
lifecycle = models.IntegerField(choices=lifecycle_stages, default=0, verbose_name="Lifecycle Stage")
software_version = models.CharField(max_length=12, blank=True, null=True, verbose_name='Software Version')
model_number = models.CharField(max_length=12, blank=True, null=True, verbose_name='Model Number')
activation_code = models.CharField(max_length=5, blank=True, null=True, verbose_name='Activation Code')
# Relationships
owner = models.ForeignKey(Company, related_name='devices', verbose_name='Device Owner',
blank=True, null=True, on_delete=models.SET_NULL)
callcenter = models.ForeignKey(Company, related_name='monitored_devices', verbose_name='Call Center',
on_delete=models.CASCADE, blank=True, null=True)
# Location & Address Details
location = models.CharField(max_length=255, blank=True, verbose_name='Device Identifier')
address = models.CharField(max_length=255, verbose_name="Street Address", blank=True)
address2 = models.CharField(max_length=255, verbose_name="Apartment, Unit, Suite, or Floor", blank=True)
address_locality = models.CharField(max_length=255, verbose_name="City/Town", blank=True)
address_state = models.CharField(max_length=255, verbose_name="State", blank=True)
address_postcode = models.CharField(max_length=55, verbose_name="Zip Code", blank=True)
address_country = models.CharField(max_length=55, verbose_name="Country", blank=True)
# Call-in Phone Number
phone_number = models.CharField(max_length=14, blank=True, null=True, verbose_name="Call-in Phone Number",
validators=[
RegexValidator(
regex=r"^\(\d{3}\)\s\d{3}-\d{4}$",
message='Phone number format is not valid, try (000) 000-0000',
),
])
# Timestamps
date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
date_last_online = models.DateTimeField(blank=True, null=True)
objects = models.Manager()
class Meta:
verbose_name = "Device"
verbose_name_plural = "Devices"
ordering = ['uuid']
def __str__(self):
return self.uuid
def set_idle_state(self):
self.state = 'idle'
def set_requested_state(self):
self.state = 'requested'
def set_active_state(self):
self.state = 'active'
#property
def full_address(self):
def check_address_parts(value):
if value == '' or value == ',':
return False
else:
return True
address_parts = [
"%s," % self.address,
"%s," % self.address2,
"%s" % self.address_locality,
"%s," % self.address_state,
"%s" % self.address_postcode,
]
return ' '.join(filter(check_address_parts, address_parts))
#property
def location_and_full_address(self):
if self.location:
if self.full_address:
return '{} - {}'.format(self.full_address, self.location)
return self.location
return self.full_address
#property
def entry(self):
address = '<div class="fs-6">{}</div>'.format(escape(self.full_address)) if self.full_address else ''
location = '<div class="fs-6">{}</div>'.format(escape(self.location)) if self.location else ''
return ' '.join((address, location))
#property
def connect(self):
owner = '' if not self.owner else self.owner.connect
callcenter = '' if not self.callcenter else self.callcenter.connect
return {
'type': 'device',
'pk': self.pk,
'uuid': self.uuid,
'state': self.state,
'first_name': '',
'last_name': '',
'owner': owner,
'callcenter': callcenter,
'initial': 'D',
'display_name': 'Device {}'.format(self.uuid),
'location': escape(self.location),
'address': escape(self.full_address),
'connect_version': settings.CONNECT_VERSION,
'url_live_call': reverse('device_live_call', args=[self.uuid]),
'theme': self.theme.settings,
}
#property
def connect_json(self):
return json.dumps(self.connect)
#property
def connect_version(self):
return settings.CONNECT_VERSION
pre_save.connect(uuid_pre_save_generator, sender=Device)
post_save.connect(alert_device_update, sender=Device)
class DeviceId(models.Model):
STAGED = 0
REQUESTED = 1
CAPTURED = 2
EXPIRED = 3
device_uuid_status = [
(STAGED, 'Staged'),
(REQUESTED, 'Requested'),
(CAPTURED, 'Captured'),
(EXPIRED, 'Expired'),
]
uuid = models.CharField(max_length=7, blank=True, unique=True, verbose_name='Device ID')
status = models.IntegerField(choices=device_uuid_status, default=0, verbose_name="ID Status")
programmer = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='programmer', null=True,
on_delete=models.PROTECT, verbose_name='Programmer')
# Timestamps
date_created = models.DateTimeField(auto_now_add=True)
date_requested = models.DateTimeField(blank=True, null=True)
date_captured = models.DateTimeField(blank=True, null=True)
date_expired = models.DateTimeField(blank=True, null=True)
objects = models.Manager()
class Meta:
verbose_name = "Device Id"
verbose_name_plural = "Device Ids"
ordering = ['date_created']
pre_save.connect(uuid_pre_save_generator, sender=DeviceId)
Well, I figured it out. For some reason the BigAutoField was set in the apps.py file in the app
from django.apps import AppConfig
class DeviceConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'device'
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)
I have those models and serializers:
models.py
class CompanyUserProfile(AbstractUserProfile):
guid = models.UUIDField(verbose_name=_('GUID'), unique=True)
organization = models.ForeignKey(
verbose_name=_('organization'),
to='my_company.Organization',
related_name='user_profiles',
null=True,
blank=True,
)
position = models.ForeignKey(
verbose_name=_('position'),
to='my_company.Position',
related_name='user_profiles',
null=True,
blank=True,
)
store_pcs = models.ManyToManyField(
verbose_name=_('Store PCs'),
to='my_company.StorePC',
related_name='user_profiles',
)
#
location = models.ForeignKey(
verbose_name=_('WorkLocation'),
to='my_company.WorkLocation',
related_name='user_profiles',
null=True,
blank=True,
)
class StorePC(AbstractOrganizationalUnit):
store_number = models.CharField(
max_length=50,
editable=False,
help_text=_("..."),
null=True,
unique=True
)
anchor = models.BooleanField(
default=False,
editable=False,
help_text=_('...'),
)
#python_2_unicode_compatible
class Transcript(TimeStampedModel):
csod_profile = models.ForeignKey(
CompanyUserProfile,
null=True, blank=True,
related_name='transcripts',
verbose_name="Company Profile"
)
learning_objective = models.ForeignKey(
LearningObjective,
verbose_name="Learning objective",
related_name="transcripts"
)
transcript_status = models.ForeignKey(
TranscriptStatus,
verbose_name=""
)
transcript_status_last_modified = models.DateTimeField(
verbose_name="Transcript Status last modified",
null=True, blank=True
)
brand = models.ForeignKey(
Score,
null=True, blank=True,
verbose_name="Score",
related_name="transcripts"
)
passed = models.NullBooleanField(
verbose_name="Has passed"
)
requested_on = models.DateTimeField(
verbose_name="Requested on",
null=True, blank=True
)
registered_on = models.DateTimeField(
verbose_name="Registered on",
null=True, blank=True
)
assigned_on = models.DateTimeField(
verbose_name="Assigned on",
null=True, blank=True
)
due_date = models.DateTimeField(
verbose_name="Due date",
null=True, blank=True
)
completed_on = models.DateTimeField(
verbose_name="Completed on",
null=True, blank=True
)
custom_dashboard = models.BooleanField(
verbose_name="",
default=True
)
completion_percentage = models.PositiveSmallIntegerField(
verbose_name="",
null=True, blank=True
)
timestamp = models.DateTimeField(
verbose_name="Timestamp"
)
serializers.py
class TranscriptSerializer(serializers.ModelSerializer):
id = serializers.CharField(source='get_id', read_only=True)
value = serializers.IntegerField(source='get_title', read_only=True)
# segment_id = serializers.CharField(default="N/A", read_only=True)
class Meta:
model = Transcript
fields = ('id', 'value')
def get_title(self, obj):
return obj.learning_objective.title
def get_id(self, obj):
return obj.learning_objective.lo_id
class PeopleSerializer(serializers.ModelSerializer):
name = serializers.SerializerMethodField('get_full_name')
title = serializers.SerializerMethodField('get_position')
los = TranscriptSerializer(source='transcripts', many=True)
class Meta:
model = CompanyUserProfile
fields = ('id', 'name', 'title', 'los')
def get_full_name(self, obj):
return obj.user.get_full_name()
def get_position(self, obj):
return obj.position.name
class StoreSerializer(serializers.ModelSerializer):
people = PeopleSerializer(source='user_profiles', many=True)
class Meta:
model = StorePC
fields = ('id', 'name', 'people')
however an API query return a result where attribute 'los' is always empty:
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 11405,
"name": "... ",
"people": [
{
"id": 887,
"name": "AAA BBB",
"title": "... title 1",
"los": [
{}
]
},
{
"id": 9263,
"name": "XYZ XYZ",
"title": "title 3",
"los": [
{}
]
},
...
}
does anybody see any errors that I don't see?
Thank You