Code

Opened 6 years ago

Closed 6 years ago

#6575 closed (invalid)

Changeset 7098 brakes model class creation if one uses own django.db.model.base.Model class

Reported by: moep Owned by: mtredinnick
Component: Database layer (models, ORM) Version: master
Severity: Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: UI/UX:

Description

Changeset 7098 brakes model class creation if one uses an own django.db.model.base.Model replacement class.

The problem is that ModelBase.__new__ assumes that a class that is a subclass of Model automatically has a _meta attribute.
That is not necessarily the case. If you use a custom replacement for Model that extends Model it is a subclass of Model but
doesn't have a meta attribute.

file mymodel.py

from django.db import modfrom django.db import models

class MyModelBase(models.base.ModelBase):
	pass

class MyModel(models.base.Model):
	pass

class Foo(MyModel):
	pass
$ import mymodel
>>> AttributeError: type object 'MyModel' has no attribute '_meta'

The problem is that you cannot so easily decide whether a class that extends Model is a Model Class or not. You could
of course check whether Class has a _meta attribute but that isn't a nice solution.

I think the only clean way to test if a class that extends Model is actually a Model is to use a special class from which
you can derive. But that's is why Model exists in the first place (except for metaclassing) I think.

So we need a design decision here, whether we add a new class that extends and replaces models.Model, we add a "ugly"
test if class has a _meta attribute or don't allow replacing model.Model at all.


Attachments (0)

Change History (3)

comment:1 Changed 6 years ago by moep

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

One can add a Fake meta class to work around this problem.

from django.db import modfrom django.db import models

class MyModelBase(models.base.ModelBase):
	pass

class FakeMeta(object):
	parents = []
	fields = []

class MyModel(models.base.Model):
	_meta = FakeMeta()

class Foo(MyModel):
	pass

comment:2 Changed 6 years ago by mtredinnick

  • Owner changed from nobody to mtredinnick

comment:3 Changed 6 years ago by mtredinnick

  • Resolution set to invalid
  • Status changed from new to closed

The problem here isn't subclasses of Model. Otherwise, for example, the model_inheritance tests wouldn't even import properly.

The problem with your example is that you've replaced the metaclass without replacing the functionality. ModelBase ensures that every subclass of Model has a _meta attribute. You've removed that condition and so your code breaks. If you're going to replace the metaclass, you must continue to fill in the class structure correctly in your replacement __new__.

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
as The resolution will be set. Next status will be 'closed'
The resolution will be deleted. Next status will be 'new'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.