#31596: ForeignKey.validate() should validate using the base manager.
-------------------------------------+-------------------------------------
Reporter: Jon Dufresne | Owner: nobody
Type: Bug | Status: closed
Component: Database layer | Version: dev
(models, ORM) |
Severity: Normal | Resolution: fixed
Keywords: model form | Triage Stage: Ready for
| checkin
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by Iván Brizuela):
This change results in a breaking change or a “gotcha” for validations
that depended on proxy model classes.
See this pseudocode setup:
{{{
class AccountDocument(models.Model):
INVOICE_TYPE = 'I'
PAYMENT_TYPE = 'P'
DOCUMENT_TYPES = [
(INVOICE_TYPE, 'Invoice'),
(PAYMENT_TYPE, 'Payment'),
]
document_type = models.CharField(
max_length=1,
choices=DOCUMENT_TYPES,
default=INVOICE_TYPE)
account: str
total: Decimal
class Invoice(AccountDocument):
# On insert / update guarantee that document_type == INVOICE_TYPE
objects: Custom manager filters AccountDocument by document_type ==
INVOICE_TYPE
class Meta:
proxy = True
class Payment(AccountDocument):
# On insert / update guarantee that document_type == PAYMENT_TYPE
objects: Custom manager filters AccountDocument by document_type ==
PAYMENT_TYPE
class Meta:
proxy = True
class Receipt(models.Model):
invoice: Invoice
payment: Payment
amount: Decimal
}}}
When using this setup:
{{{
invoice:Invoice = Invoice(document_type=INVOICE_TYPE, account='xyz',
total=Decimal('100'))
payment:Payment = Payment(document_type=PAYMENT_TYPE, account='xyz',
total=Decimal('50'))
invoice.save()
payment.save()
# Setting error condition to be tested: invalid account documents are
assigned as receipt properties
receipt = Receipt(
invoice=payment, # invalid!
payment=invoice, # invalid!
amount=Decimal('50'))
receipt.full_clean()
}}}
**Expected (Before 3.2):**
ValidationError is raised, as neither invoice:Payment nor payment:Invoice
exist when looked up by their default manager.
**Actual (Since 3.2):**
No exception is raised, which is unexpected given the field definitions.
Both the example given to request this change and the one I am providing,
stretch the base idea behind proxy models: “only change the Python
behavior of a model”.
Semantically, I don't think an ArticleManager should filter by 'archived'
status. It would be preferred to have three different managers or proxy
models:
* Article with ArticleManager (not filtered by archived status): Use this
when any article can be picked from a list.
* ActiveArticle(proxy) with ActiveArticleManager (filter archived==False):
Enforce custom rules, like editing an article.
* ArchivedArticle(proxy) with ArchivedArticleManager (filter
archived==True): Enforce other rules, like read-only status.
**Keeping a proxy model self-contained by using its default manager to
validate a ForeignKey seems a more valuable use case to me.**
Consider adding the following note to the release notes in 3.2:
https://docs.djangoproject.com/en/4.0/releases/3.2/
> (existent) ForeignKey.validate() now uses _base_manager rather than
_default_manager to check that related instances exist.
> (addition) This might break validations enforced by using proxy model
classes in ForeignKey field definitions.
--
Ticket URL: <https://code.djangoproject.com/ticket/31596#comment:10>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/0107018196cc683b-a65c3e61-c690-4588-b2c5-ca1bd1aea350-000000%40eu-central-1.amazonses.com.