Creating a class based model validator

I am trying to create an OpeningHours Model which will have a foreign key to a Vendor model as shown below.

An then I want to put a validator in the opening time field so that if a vendor tries to put an opening time that is inside the range of another opening hour object then it will raise a ValidationError.

I can do it inside the clean() method, but I want to associate it with the Open field and also I will associate a closing_time_repitition_validator which will check the closing time and give precious errors.

But I am getting error as shown in the Image below. So how am I supposed to do this?

CODE STARTS HERE
@deconstructible
class OpeningOverlapValidator(object):
    day = -1
    vendor = None

    def __init__(self, day, vendor):
        self.day = day
        self.vendor = vendor

    def __call__(self, value):
        print(type(self.vendor))
        open_slabs = OpeningHours.objects.filter(vendor=self.vendor, day=self.day)
        for slab in open_slabs:
            if value >= slab.open and value < slab.close:
                raise ValidationError(_('There is an overlap in the opening hour for this day'), code=500)
    
    def __eq__(self, other):
        return isinstance(other, self.__class__) and self.day == other.day and self.vendor == other.vendor

class OpeningHours(models.Model):
    vendor = models.ForeignKey(Vendor, on_delete=models.CASCADE)
    day = models.CharField(choices=DAY_CHOICES)
    open = models.FloatField(choices=TIME_CHOICES, validators=[OpeningOverlapValidator(day, vendor)])
    close = models.FloatField(choices=TIME_CHOICES)
    is_closed = models.BooleanField(default=False)

    def __str__(self) -> str:
        return self.vendor.vendor_name
    
    def save(self, *args, **kwargs):
        self.full_clean()
        return super(OpeningHours, self).save(*args, **kwargs)

    def clean(self):
        if self.open >= self.close:
            raise ValidationError(_('Restaurant must Open before Closing'), code=500)
        super(OpeningHours, self).clean()

Side note: When posting code or errors here, please enclose the code between lines of three backtick - ` characters. This means you’ll have a line of ```, then the code (or error message), then another line of ```. (I’ve taken the liberty of editing your original code for this.)

If you’re asking for assistance with an error, please post the complete error and traceback from the console window where you’re running runserver. There tends to be more useful information in that than a partial screenshot of the error being reported on a page.

You may find this cleaner and easier if you do this in your form handling than in the model.

I can do it in the form. But what if someone tries to post in the db from somewhere other than the form. Like using self written script. That will not be validated then by the form. That’s why I want to validate the model itself before making any entry in it.

And here is the error from console.

Internal Server Error: /admin/vendor/openinghours/add/
Traceback (most recent call last):
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\fields\__init__.py", line 2112, in get_prep_value
   return int(value)
          ^^^^^^^^^^
TypeError: int() argument must be a string, a bytes-like object or a real number, not 'ForeignKey'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
   response = get_response(request)
              ^^^^^^^^^^^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\core\handlers\base.py", line 197, in _get_response
   response = wrapped_callback(request, *callback_args, **callback_kwargs)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\contrib\admin\options.py", line 714, in wrapper
   return self.admin_site.admin_view(view)(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\utils\decorators.py", line 188, in _view_wrapper
   result = _process_exception(request, e)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\utils\decorators.py", line 186, in _view_wrapper
   response = view_func(request, *args, **kwargs)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\views\decorators\cache.py", line 80, in _view_wrapper
   response = view_func(request, *args, **kwargs)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\contrib\admin\sites.py", line 240, in inner  
   return view(request, *args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\contrib\admin\options.py", line 1941, in add_view
   return self.changeform_view(request, None, form_url, extra_context)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\utils\decorators.py", line 48, in _wrapper   
   return bound_method(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\utils\decorators.py", line 188, in _view_wrapper
   result = _process_exception(request, e)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\utils\decorators.py", line 186, in _view_wrapper
   response = view_func(request, *args, **kwargs)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\contrib\admin\options.py", line 1802, in changeform_view
   return self._changeform_view(request, object_id, form_url, extra_context)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\contrib\admin\options.py", line 1847, in _changeform_view
   form_validated = form.is_valid()
                    ^^^^^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\forms\forms.py", line 197, in is_valid       
   return self.is_bound and not self.errors
                                ^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\forms\forms.py", line 192, in errors
   self.full_clean()
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\forms\forms.py", line 329, in full_clean     
   self._post_clean()
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\forms\models.py", line 495, in _post_clean   
   self.instance.full_clean(exclude=exclude, validate_unique=False)
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\base.py", line 1512, in full_clean 
   self.clean_fields(exclude=exclude)
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\base.py", line 1564, in clean_fields
   setattr(self, f.attname, f.clean(raw_value, self))
                            ^^^^^^^^^^^^^^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\fields\__init__.py", line 837, in clean
   self.run_validators(value)
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\fields\__init__.py", line 789, in run_validators
   v(value)
 File "X:\Multi Vendor Restaurant\Restaurant\vendor\models.py", line 78, in __call__
   open_slabs = OpeningHours.objects.filter(vendor=self.vendor, day=self.day)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\manager.py", line 87, in manager_method
   return getattr(self.get_queryset(), name)(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\query.py", line 1476, in filter    
   return self._filter_or_exclude(False, args, kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\query.py", line 1494, in _filter_or_exclude
   clone._filter_or_exclude_inplace(negate, args, kwargs)
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\query.py", line 1501, in _filter_or_exclude_inplace
   self._query.add_q(Q(*args, **kwargs))
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\sql\query.py", line 1600, in add_q 
   clause, _ = self._add_q(q_object, self.used_aliases)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\sql\query.py", line 1632, in _add_q
   child_clause, needed_inner = self.build_filter(
                                ^^^^^^^^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\sql\query.py", line 1546, in build_filter
   condition = self.build_lookup(lookups, col, value)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\sql\query.py", line 1376, in build_lookup
   lookup = lookup_class(lhs, rhs)
            ^^^^^^^^^^^^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\lookups.py", line 30, in __init__  
   self.rhs = self.get_prep_lookup()
              ^^^^^^^^^^^^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\fields\related_lookups.py", line 156, in get_prep_lookup
   self.rhs = target_field.get_prep_value(self.rhs)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\fields\__init__.py", line 2114, in get_prep_value
   raise e.__class__(
TypeError: Field 'id' expected a number but got <django.db.models.fields.related.ForeignKey: vendor>.
[26/Jan/2024 08:49:06] "POST /admin/vendor/openinghours/add/ HTTP/1.1" 500 218387

Yes, and if someone uses something like bulk_create or bulk_update, or raw SQL, it’s going to bypass your model code as well.

If this is a serious concern, if you’re in an environment where you’re unable to manage the codebase to prevent mistakes like this, then you should implement this as a trigger within the database itself, because there’s nothing you can do within Django that can protect every edge case.

So the root error appears to be here:

It’s looking like whatever is processing these validators does not like the foreign key reference. You could possibly resolve this by changing your query syntax to explicitly identify this as using the id field.

e.g.: open_slabs = OpeningHours.objects.filter(vendor_id=self.vendor.id, day=self.day)

Side note: You can also improve the performance of this process by adding the tests for value being between the open and close within the query itself and looking for the existance of a matching row - this would indicate an overlap.

Yes, and if someone uses something like `bulk_create` or `bulk_update`, or raw SQL, it’s going to bypass your model code as well.

If this is a serious concern, if you’re in an environment where you’re unable to manage the codebase to prevent mistakes like this, then you should implement this as a trigger within the database itself, because there’s nothing you can do within Django that can protect every edge case.

This was really a very nice suggestion. I will definitely try to do this. But for now I am concerned with the backend only, not the database. So I can do my best for handling any security breaches.

I tried this but it’s still giving the same error
open_slabs = OpeningHours.objects.filter(vendor_id=self.vendor.id, day=self.day)

Internal Server Error: /admin/vendor/openinghours/add/
Traceback (most recent call last):
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\fields\__init__.py", line 2112, in get_prep_value
    return int(value)
           ^^^^^^^^^^
TypeError: int() argument must be a string, a bytes-like object or a real number, not 'ForeignKey'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\core\handlers\base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\contrib\admin\options.py", line 714, in wrapper
    return self.admin_site.admin_view(view)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\utils\decorators.py", line 188, in _view_wrapper
    result = _process_exception(request, e)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\utils\decorators.py", line 186, in _view_wrapper
    response = view_func(request, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\views\decorators\cache.py", line 80, in _view_wrapper
    response = view_func(request, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\contrib\admin\sites.py", line 240, in inner  
    return view(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\contrib\admin\options.py", line 1941, in add_view
    return self.changeform_view(request, None, form_url, extra_context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\utils\decorators.py", line 48, in _wrapper   
    return bound_method(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\utils\decorators.py", line 188, in _view_wrapper
    result = _process_exception(request, e)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\utils\decorators.py", line 186, in _view_wrapper
    response = view_func(request, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\contrib\admin\options.py", line 1802, in changeform_view
    return self._changeform_view(request, object_id, form_url, extra_context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\contrib\admin\options.py", line 1847, in _changeform_view
    form_validated = form.is_valid()
                     ^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\forms\forms.py", line 197, in is_valid       
    return self.is_bound and not self.errors
                                 ^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\forms\forms.py", line 192, in errors
    self.full_clean()
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\forms\forms.py", line 329, in full_clean     
    self._post_clean()
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\forms\models.py", line 495, in _post_clean   
    self.instance.full_clean(exclude=exclude, validate_unique=False)
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\base.py", line 1512, in full_clean 
    self.clean_fields(exclude=exclude)
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\base.py", line 1564, in clean_fields
    setattr(self, f.attname, f.clean(raw_value, self))
                             ^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\fields\__init__.py", line 837, in clean
    self.run_validators(value)
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\fields\__init__.py", line 789, in run_validators
    v(value)
  File "X:\Multi Vendor Restaurant\Restaurant\vendor\models.py", line 78, in __call__
    open_slabs = OpeningHours.objects.filter(vendor_id=self.vendor, day=self.day)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\manager.py", line 87, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\query.py", line 1476, in filter    
    return self._filter_or_exclude(False, args, kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\query.py", line 1494, in _filter_or_exclude
    clone._filter_or_exclude_inplace(negate, args, kwargs)
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\query.py", line 1501, in _filter_or_exclude_inplace
    self._query.add_q(Q(*args, **kwargs))
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\sql\query.py", line 1600, in add_q 
    clause, _ = self._add_q(q_object, self.used_aliases)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\sql\query.py", line 1632, in _add_q
    child_clause, needed_inner = self.build_filter(
                                 ^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\sql\query.py", line 1546, in build_filter
    condition = self.build_lookup(lookups, col, value)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\sql\query.py", line 1376, in build_lookup
    lookup = lookup_class(lhs, rhs)
             ^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\lookups.py", line 30, in __init__  
    self.rhs = self.get_prep_lookup()
               ^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\fields\related_lookups.py", line 156, in get_prep_lookup
    self.rhs = target_field.get_prep_value(self.rhs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\fields\__init__.py", line 2114, in get_prep_value
    raise e.__class__(
TypeError: Field 'id' expected a number but got <django.db.models.fields.related.ForeignKey: vendor>.
[26/Jan/2024 11:37:34] "POST /admin/vendor/openinghours/add/ HTTP/1.1" 500 218292

I found out that when I am passing the variables to the constructor of my validator here open = models.FloatField(choices=TIME_CHOICES, validators=[OpeningOverlapValidator(day, vendor)]) it’s not passing the values with which it is attempting to create the model, instead it’s just simply passing the model.ForeignKey constructor.

As validating this field require values of other fields the problem is happening here. I am not able to pass the value of other fields in the validator.

vendor in your validator is, indeed, the ForeignKey field, not the value of vendor field for a particular instance: this is how you constructed the validator on following line:

Your validator is a cross-field validator (vendor vs open), so it cannot be attached to one specific field the way you did. Such validation should happen in the clean method and not at signle field level

Yeah that’s right. I should go for the clean() method.

But thank you so much. I have discovered two new idea from this chat. And it has improved the performance of my code. Thank you so much again

You didn’t make the change I suggested.

What I wrote:

What the error message is reporting that you have:

These two are not the same.

Yeah sorry for the mistake. Now I tried again with this code open_slabs = OpeningHours.objects.filter(vendor_id=self.vendor.id, day=self.day)

And here is the error code

  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\core\handlers\base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\contrib\admin\options.py", line 714, in wrapper
    return self.admin_site.admin_view(view)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\utils\decorators.py", line 188, in _view_wrapper
    result = _process_exception(request, e)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\utils\decorators.py", line 186, in _view_wrapper
    response = view_func(request, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\views\decorators\cache.py", line 80, in _view_wrapper
    response = view_func(request, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\contrib\admin\sites.py", line 240, in inner  
    return view(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\contrib\admin\options.py", line 1941, in add_view
    return self.changeform_view(request, None, form_url, extra_context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\utils\decorators.py", line 48, in _wrapper   
    return bound_method(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\utils\decorators.py", line 188, in _view_wrapper
    result = _process_exception(request, e)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\utils\decorators.py", line 186, in _view_wrapper
    response = view_func(request, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\contrib\admin\options.py", line 1802, in changeform_view
    return self._changeform_view(request, object_id, form_url, extra_context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\contrib\admin\options.py", line 1847, in _changeform_view
    form_validated = form.is_valid()
                     ^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\forms\forms.py", line 197, in is_valid       
    return self.is_bound and not self.errors
                                 ^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\forms\forms.py", line 192, in errors
    self.full_clean()
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\forms\forms.py", line 329, in full_clean     
    self._post_clean()
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\forms\models.py", line 495, in _post_clean   
    self.instance.full_clean(exclude=exclude, validate_unique=False)
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\base.py", line 1512, in full_clean 
    self.clean_fields(exclude=exclude)
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\base.py", line 1564, in clean_fields
    setattr(self, f.attname, f.clean(raw_value, self))
                             ^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\fields\__init__.py", line 837, in clean
    self.run_validators(value)
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\fields\__init__.py", line 789, in run_validators
    v(value)
  File "X:\Multi Vendor Restaurant\Restaurant\vendor\models.py", line 78, in __call__
    open_slabs = OpeningHours.objects.filter(vendor_id=self.vendor.id, day=self.day)
                                                       ^^^^^^^^^^^^^^
AttributeError: 'ForeignKey' object has no attribute 'id'
[26/Jan/2024 21:58:21] "POST /admin/vendor/openinghours/add/?_changelist_filters=o%3D2 HTTP/1.1" 500 159090

Hmm… You can try changing it to:
open_slabs = OpeningHours.objects.filter(vendor_id=self.vendor_id, day=self.day)

I tried this open_slabs = OpeningHours.objects.filter(vendor_id=self.vendor_id, day=self.day)

And here is the error

Internal Server Error: /admin/vendor/openinghours/add/
Traceback (most recent call last):
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\core\handlers\base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\contrib\admin\options.py", line 714, in wrapper
    return self.admin_site.admin_view(view)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\utils\decorators.py", line 188, in _view_wrapper
    result = _process_exception(request, e)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\utils\decorators.py", line 186, in _view_wrapper
    response = view_func(request, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\views\decorators\cache.py", line 80, in _view_wrapper
    response = view_func(request, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\contrib\admin\sites.py", line 240, in inner  
    return view(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\contrib\admin\options.py", line 1941, in add_view
    return self.changeform_view(request, None, form_url, extra_context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\utils\decorators.py", line 48, in _wrapper   
    return bound_method(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\utils\decorators.py", line 188, in _view_wrapper
    result = _process_exception(request, e)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\utils\decorators.py", line 186, in _view_wrapper
    response = view_func(request, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\contrib\admin\options.py", line 1802, in changeform_view
    return self._changeform_view(request, object_id, form_url, extra_context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\contrib\admin\options.py", line 1847, in _changeform_view
    form_validated = form.is_valid()
                     ^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\forms\forms.py", line 197, in is_valid       
    return self.is_bound and not self.errors
                                 ^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\forms\forms.py", line 192, in errors
    self.full_clean()
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\forms\forms.py", line 329, in full_clean     
    self._post_clean()
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\forms\models.py", line 495, in _post_clean   
    self.instance.full_clean(exclude=exclude, validate_unique=False)
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\base.py", line 1512, in full_clean 
    self.clean_fields(exclude=exclude)
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\base.py", line 1564, in clean_fields
    setattr(self, f.attname, f.clean(raw_value, self))
                             ^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\fields\__init__.py", line 837, in clean
    self.run_validators(value)
  File "X:\Multi Vendor Restaurant\.venv\Lib\site-packages\django\db\models\fields\__init__.py", line 789, in run_validators
    v(value)
  File "X:\Multi Vendor Restaurant\Restaurant\vendor\models.py", line 77, in __call__
    open_slabs = OpeningHours.objects.filter(vendor_id=self.vendor_id, day=self.day)
                                                       ^^^^^^^^^^^^^^
AttributeError: 'OpeningOverlapValidator' object has no attribute 'vendor_id'
[27/Jan/2024 01:43:58] "POST /admin/vendor/openinghours/add/?_changelist_filters=o%3D2 HTTP/1.1" 500 159168    

Now that I’ve taken the time to look at this more closely, this is making more sense to me now.

The problem is that you’re actually calling the validator in your field definition instead of providing the callable.

You have:

This is actually calling that class at the time of definition instead of supplying the callable in the list.

Notice the docs for the usage of a validator:

class MyModel(models.Model):
    even_field = models.IntegerField(validators=[validate_even])

It’s not calling that function here - it’s passing the callable itself. Also note that a validator only accepts one parameter - the value being validated.

So yes, you need to find a different way to do this.

That is a function based validator. But what I want is something like RegexValidator. And there are no examples on how to use them in practice. Because I need to supply arguments to the class.

You can’t supply runtime arguments in model validators. They don’t work that way.

What you’re seeing in the docs regarding the regex validator are the values used to initialize the class. They’re variables used to configure the class when the class is instantiated - they’re not dynamic values passed to the validation function when the validator is being executed.