Well there are several options here. The following "list" is not exhaustive, although it might perhaps provide a few ideas, and variants can be constructed based on these ideas.
Leaving the models like it is
In that case we thus model it like:
+---------+ 1 N +------------+
| Country |----------| CountryLaw |
+---------+ 1 N +-----------+
| Region |----------| RegionLaw |
Here we thus constructed two
Laws. Although we can of course superclass the two
Laws, it means that each has its own type.
The advantage is that if the two have specific semantics, for example a
CountryLaw should be handled quite differently from a
RegionLaw, then that is easier to implement. Furthermore if a
CountryLaw has specific fields, that are for instance not important for
RegionLaws (or vice versa), then we avoid wasting diskspace on
NULL values (or other placeholders).
The downside is that if we for example want to query for laws that are part of
'Germany', we have to this in two steps: query for the
CountryLaws of germany, and query for the
RegionLaws of all regions of
Germany. This can also easily get out of hand if you also have
Working with Proxy regions
Here we consider all
Laws to be made for a specific
Region, but the trick is that we construct a
Region that acts like the entire country. So besides
'Bavaria', we use a virtual region
'Germany' that will represent the entire country.
We can then introduce a single
Law model, that is attached to
Region. If we frequently need to make the distinction between a region, and a country, we can add a field
is_country that for example specifies if this is a "country proxy" or a real region:
| Country |
+-------------------+ 1 N +-----+
| Region |----------| Law |
| is_country : bool |
The advantage is that we have only one
Law object, and therefore the design is easier. Furthermore it is easy to query for the laws that map on a country (including or excluding regions).
The downside is that if country
Laws and region
Laws differ significantly, then this will result in an awful amount of checking (each time looking at whether the attached region is really a region, or a country), and furthermore it can result in a lot of unused fields. Furthermore if we enable more and more layers, we need to introduce more and more proxy objects. If we would for example would use three layers (
SubRegion), then we need to construct for every country a "proxy" region (that contains a proxy subregion as well), and for every region, a proxy subregion. So if there are n countries and m regions, this would result in 2×n + m proxy objects, which also results in data duplication (we repeat the name of the country/region multiple times, and if later a country or region is renamed, it will result in some pain to update all those proxies).
Working with a tree-like structure
In case the number of levels can be large, or dynamic (in the sense that some "areas"* have subregions, whereas for others there is no region), or we want to handle all these levels in a uniform way, we could decide to use a tree-like structure.
Here we define a model, for example
Area can have a
parent, which is an
Area as well, we can construct a tree-structure that way. For example something like:
parent = ForeignKey('app.Area', on_delete=models.SET_NULL, related_name='children')
We can then attach to each
Area zero, one or more laws. So the model looks like:
+------+ 1 N +-----+
| Area |----------| Law |
The advantage is that here we have one model for
Subregion, etc. Furthermore we can implement a hierarchy exactly the way we want where for example some (small) countries have no
Regions (like for example "city states" like Vatican City, Singapore, etc.). Furthermore the
Law object will link to an "area"-like object. It is also easy to obtain the laws attached to a certain area.
A problem is however that it is hard to obtain all the
Laws of a country, its regions, its subregions, etc. This can be handled however, for example by designing a many-to-many table that encodes the transitive closure of this tree structure: this many-to-many relation then will contain the links a country with all its regions, subregions, etc., but still that is not very elegant. It also means that all these
Area instances are represented uniformly. Therefore if we want to add for example a list with official languages to the
Area, all areas have "official languages", whereas probably most subregions simply would "inherit" the official language of their country.
GenericForeignKey in a
Law object (Django feature)
Django also has a special sort of relation called the
GenericForeignKey [doc]. This may look like a great feature for problems like this, but I would really advice to avoid such relations as much as possible.
By default Django add an implicit primary key to every model: if the developer does not specify a field with
primary_key=True. Django will automatically add an
IntegerField that will specify an identifier as primary key. It is thus reasonable to assume that most models have an
IntegerField as primary key (in fact it israther un-Django to specify another primary key). We can also generate a list that maps every model to an integer: we could for example say that
0 maps to
1 maps to
This means that most model instances can be identified by two integers: one integer that specifies the model, and one that specifies the primary key of the corresponding model. For example
(0, 14) is the
User with primary key
14 (given we use the "lookup table" defined in the paragraph above). This is a powerful concept: we can thus use two database columns to store these integers, and each time let Django fetch the object. This is the idea behind a
GenericForeignKey. We can thus define a
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
area = GenericForeignKey('content_type', 'object_id')
So this means that now our
Law stores two real fields: a
content_type and an
object_id, and if we query for
some_law.area, Django will fetch an object that corresponds with these two integers.
We thus could use this to refer to a
Subregion, which can be useful in case we can refer to a variety of models. But there are a lot of disadvantages. The main problem is that such relations are usually very cumbersome in the case we want to use them in a query. Indeed: say we want to join our
Law model with the
area field. Then with which table should we join? The
SubRegion? What if one
Law refers to a
Country whereas the other refers to a
Region? So typically we can not
Furthermore the model itself does not guarantee that the
GenericForeignKey will always refer to an "area"-like object. It could refer to another
Law, to a
Criminal, etc. So as a result you are responsible to write sane logic that will always make sure the relations make sense. Although this looks easy, it can be hard to maintain good sane relations. Most databases can not check whether the foreign key refers to a valid object, since there is no
FOREIGN KEY constraint, since the "target table" is unknown.
Although there are a lot of drawbacks, in some cases a
GenericForeignKey can be an elegant solution to certain problems, but one has to be careful.
Although there is - as far as I know - no generally accepted way to specify such relations in a diagram, it could look like this:
| Country |
| 1 .
| N .
+---------+ . +-----+
| Region |. . . . . | Law |
+---------+ 1 N +-----+