DRF: Отношение OneToOne с unique_together
Я пытаюсь понять, как использовать отношение OnetoOne с unique_together. В настоящее время я пытаюсь использовать их в сериализаторе и представлении, но безуспешно.
Модели:
class Employees(models.Model):
name = models.CharField()
position = models.CharField()
phone = models.IntegerField()
class WorkSchedule(models.Model):
employee = models.OneToOneField('Employees')
workday = models.DateTimeField()
class Meta:
unique_together = (('employee', 'workday'),)
Первый вопрос: Поскольку отношение OneToOne, если бы у меня не было unique_together, то каждый сотрудник мог бы появиться в WorkSchedule один раз. Однако, поскольку у меня есть unique_together, это уже не так. Правильно?
Второй вопрос: Как я могу использовать такие модели для возврата всех сотрудников и их рабочих графиков, например, так
{
"employees": [
{
"employee": "John",
"workschedule": [
{
workday: "2022-03-03"
},
{
workday: "2022-03-04"
}
},
{
"employee": "Tom",
"workschedule": [
{
workday: "2022-03-03"
},
{
workday: "2022-03-04"
}
}
}
Я смог сделать это с помощью ForeignKey (без unique_together) и вложенного сериализатора, однако я не смог сделать это с помощью двух моделей, как они указаны выше.
Первый вопрос: Поскольку отношение OneToOne, если бы у меня не было
unique_together
, то каждый сотрудник мог бы появиться в WorkSchedule один раз. Однако, поскольку у меня есть unique_together, это уже не так. Правильно?
Нет: OneToOneField
- это ForeignKey
с ограничением однократности. Тот факт, что вы позже определите дополнительное ограничение, не имеет значения. Это означает, что ваш unique_together
не имеет никакого влияния: поскольку OneToOneField
уже гарантирует, что employee
уникален, это подразумевает, что комбинация с workday
будет уникальной, даже без указания unique_together
.
Таким образом, вы должны использовать ForeignKey
, поэтому:
class WorkSchedule(models.Model):
employee = models.ForeignKey('Employees', on_delete=models.CASCADE)
workday = models.DateTimeField()
class Meta:
constraints = [
models.UniqueConstraint(fields=['employee', 'workday'], name='unique_employee_workday')
]
Второй вопрос: Как я могу использовать такие модели для возврата всех сотрудников и их рабочих графиков, например, так.
Используя ForeignKey
. Здесь нет смысла использовать OneToOneField
: это имеет смысл, только если у каждого WorkSchedule
есть ровно один Employee
, а у каждого Employee
есть максимум один WorkSchedule
.
Примечание: Как говорится в документации по
unique_together
[Django-doc], ограничениеunique_together
, вероятно, станет устаревшим. Документация советует использоватьUniqueConstraint
[Django-doc] от Django constraint framework.
Пытаемся вместе понять уникальное -
Пример кода -
class WorkSchedule(models.Model):
employee = models.CharField()
workday = models.CharField()
class Meta:
unique_together = (('employee', 'workday'),)
Unique вместе здесь означает, что каждый объект WorkSchedule будет уникальным при сравнении двух полей за один раз.
Например, предположим, что первым объектом является
{
"employee" : "A"
"workday" : "A"
}
Теперь ни один другой объект не может иметь значения employee = A и workday = A объединенные.
Так что другие объекты могут иметь такие значения, как (2 случая) -
Если значение employee является A в любом другом объекте, workday должен не быть A. В противном случае он потерпит неудачу при уникальном ограничении вместе.
Также, если значение workday является A в любом другом объекте, то employee не может быть A, Иначе, это снова приведет к неудаче в unique together constrain.
Возвращаясь к вашему вопросу -
поскольку вы использовали OneToOneField для employee в WorkSchedule, то по умолчанию не будет двух одинаковых значений employee и вы можете пренебречь использованием unique_together.
Но опять же результат, который вы ожидаете, создаст два объекта WorkSchedule (из примера результата) с одинаковым сотрудником и разным рабочим днем. Поэтому вы не можете использовать OneToOneField, вам придется использовать ForeignKey для сотрудника и убедиться, что сотрудник и рабочий день уникальны вместе.
В документации также упоминается, что unique_together будет обесценен в будущем, поэтому вам придется кодировать так, как упомянул сэр @Willem Van Onsem.