Reputation: 507
Each "time range" entry of the TimeClass is dependent on each other.
They cannot overlap and start_time < end_time.
models.py
class Xyz(models.Model):
...
class TimeRangeClass(models.Model)
start_time = models.TimeField()
end_time = models.TimeField()
xyz = models.ForeignKey(Xyz)
# other fields here
def clean(self):
# Here I loop through TimeRangeClass.objects.all() and
# check for conflicts through my custom "my_validator_method".
# If there is a conflict I throw an error
#(I've since modified it to just be one single query as per Titusz advice)
for each in TimeRangeClass.objects.filter(xyz=self.xyz).exclude(id=self.id):
my_validator_method(start_time1=self.start_time,
end_time1=self.end_time,
start_time2=each.start_time,
end_time2=each.end_time)
admin.py
from .models import TimeRangeClass, Xyz
class TimeRangeClassInLine(admin.TabularInline):
model = TimeRangeClass
extra = 3
@admin.register(Xyz)
class Xyz(admin.ModelAdmin):
exclude = []
inlines = [TimeRangeClassInLine]
Problem: I can edit/add multiple TimeRangeClass's at once through the admin. But given that the models.Model clean method only evaluates 1 change at a time I can't validate multiple edits against each other.
Example:
Save an Entry1 & Entry2 without conflict
Change Entry2 to produce a validation error
Adjust Entry1 (instead of #2) so they do not overlap
This doesn't register because neither changes are written to the db.
I'm looking for a workaround.
Upvotes: 0
Views: 852
Reputation: 1477
Some hints on the problem:
You should not iterate over the full table when checking for overlapping rows. Just filter for the problematic rows... something like:
overlaps = TimeRangeClass.objects.filter(
Q(start_time__gte=self.start_time, start_time__lt=self.end_time) |
Q(end_time__gt=self.start_time, end_time__lte=self.end_time)
)
overlaps
is now a queryset that evaluates when you iterate over it and only returns the conflicting objects.
If you are using Django with postgres you should check out https://docs.djangoproject.com/es/1.9/ref/contrib/postgres/fields/#datetimerangefield.
Once you have the conflicting objects you should be able to change their start and end times within the function and save the changes. Model.save() will not automatically call the model.clean() method. But be aware, if you save an object from the Django admin it will call the model.clean() method before saving.
So something like that:
def clean():
overlaps = TimeRangeClass.overlaps.for_trc(self)
for trc_object in overlaps:
fixed_object = fix_start_end(trc_object, self)
fixed_object.save()
If you feel brave you should also read up on transactions to make the mutation of multiple objects in the database all succeed or all fail and nothing in between.
def clean():
with transaction.atomic():
# do your multi object magic here ...
Update on clarified question:
If you want to validate or pre/process data that comes from admin inlines you have to hook into the corresponding ModelAdmin
method(s). There are multiple ways to approach this. I guess the easiest would be to override ModelAdmin.save_fromset. Here you have access to all the inlineforms before they have been saved.
Upvotes: 1