1. 程式人生 > >django 設定指向自己的多對多關係,且要增加額外欄位而需自定義關係表,執行py manage.py makemigrations時報fields.E304 錯誤 .related_name

django 設定指向自己的多對多關係,且要增加額外欄位而需自定義關係表,執行py manage.py makemigrations時報fields.E304 錯誤 .related_name

 原始程式碼:

# 鄰區多對多關係表
class ZJadjacent(models.Model):
    cellfrom = models.ForeignKey(ZJcell, on_delete=models.CASCADE)
    cellto = models.ForeignKey(ZJcell, on_delete=models.CASCADE)
    # 兩個鄰區間的距離
    distance = models.IntegerField()
    # 站在源小區,看目標小區的方位角
    azi1 = models.IntegerField()
    # 站在目標小區,看源小區的方位角
    azi2 = models.IntegerField()

執行py manage.py makemigrations時報錯:

(venv) C:\Users\Administrator\PycharmProjects\cellsmap>py manage.py makemigrations
SystemCheckError: System check identified some issues:

ERRORS:
dbbackend.ZJadjacent.cellfrom: (fields.E304) Reverse accessor for 'ZJadjacent.cellfrom' clashes with reverse accessor for 'ZJadjacent.cellto'.
        HINT: Add or change a related_name argument to the definition for 'ZJadjacent.cellfrom' or 'ZJadjacent.cellto'.
dbbackend.ZJadjacent.cellto: (fields.E304) Reverse accessor for 'ZJadjacent.cellto' clashes with reverse accessor for 'ZJadjacent.cellfrom'.
        HINT: Add or change a related_name argument to the definition for 'ZJadjacent.cellto' or 'ZJadjacent.cellfrom'.

官網解釋:

ManyToManyField.related_name

Same as ForeignKey.related_name.

ForeignKey.related_name

The name to use for the relation from the related object back to this one. It's also the default value for related_query_name (the name to use for the reverse filter name from the target model). See the

related objects documentation for a full explanation and example. Note that you must set this value when defining relations on abstract models; and when you do so some special syntax is available.

 

如果想關閉反查或者反向關係,可將related_name 設為 '+'。

注意:這與多對多欄位的對稱屬性symmetrical=False不同,之前我將多對多欄位的對稱屬性symmetrical=False誤認為是關閉反查關係。詳見:

https://blog.csdn.net/qq_27361945/article/details/83181900

 

If you'd prefer Django not to create a backwards relation, set related_name to '+' or end it with '+'. For example, this will ensure that the User model won't have a backwards relation to this model:

user = models.ForeignKey(
    User,
    on_delete=models.CASCADE,
    related_name='+',
)

雖然沒有ManyToManyField的例子,但是也是一樣的,ManyToManyField相當於兩個ForeignKey

定義:

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def __str__(self):
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=200)
    email = models.EmailField()

    def __str__(self):
        return self.name

class Entry(models.Model):
    blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
    headline = models.CharField(max_length=255)
    body_text = models.TextField()
    pub_date = models.DateField()
    mod_date = models.DateField()
    authors = models.ManyToManyField(Author)
    n_comments = models.IntegerField()
    n_pingbacks = models.IntegerField()
    rating = models.IntegerField()

    def __str__(self):
        return self.headline

Following relationships "backward"

If a model has a ForeignKey, instances of the foreign-key model will have access to a Manager that returns all instances of the first model. By default, this Manager is named FOO_set, where FOO is the source model name, lowercased. This Manager returns QuerySets, which can be filtered and manipulated as described in the "Retrieving objects" section above.

舉例:

>>> b = Blog.objects.get(id=1)
>>> b.entry_set.all() # Returns all Entry objects related to Blog.

# b.entry_set is a Manager that returns QuerySets.
>>> b.entry_set.filter(headline__contains='Lennon')
>>> b.entry_set.count()

注意:沒有定義related_name的外來鍵,反查時用entry_set,即原模型的名字 Entry的小寫(class Entry(models.Model):)

一單定義了related_name='entries',反查時用entries代替entry_set。

但是多對多怎麼理解?

在反查時,如果指明ZJadjacent關係表中的兩個models.ForeignKey(ZJcell, on_delete=models.CASCADE)哪個是源小區,哪個是目標小區,通過ZJcell物件反查鄰區關係時:

ZJcell.objects.first().zjadjacent_set

就會發生混淆,因為ZJcell.objects.first()這個例項既可以是源小區,也可以是目標小區,我們到底要從它是源小區的關係表中提取資料,還是從目標小區的關係表中提取資料,從ZJcell.objects.first().zjadjacent_set中看不出來。程式就會混淆。

 

當增加related_name='from_cell_id'後,就可以這樣查:

ZJcell.objects.first().from_cell_id

這樣就知道是把ZJcell.objects.first()當做源小區,從鄰區表中提取資料,而不會提取鄰區表中ZJcell.objects.first()是目標小區的資料。

You can override the FOO_set name by setting the related_name parameter in the ForeignKey definition. For example, if the Entry model was altered to blog = ForeignKey(Blog, on_delete=models.CASCADE, related_name='entries'), the above example code would look like this:

>>> b = Blog.objects.get(id=1)
>>> b.entries.all() # Returns all Entry objects related to Blog.

# b.entries is a Manager that returns QuerySets.
>>> b.entries.filter(headline__contains='Lennon')
>>> b.entries.count()

 

修改 後的程式碼:

# 鄰區多對多關係表
class ZJadjacent(models.Model):
    cellfrom = models.ForeignKey(ZJcell, on_delete=models.CASCADE, related_name='from_cell_id')
    # 因為有兩個ZJcell外來鍵,必須對這兩個外來鍵命名為不同的名字,否則出現報錯fields.E304
    cellto = models.ForeignKey(ZJcell, on_delete=models.CASCADE, related_name='to_cell_id')
    # 兩個鄰區間的距離
    distance = models.IntegerField()
    # 站在源小區,看目標小區的方位角
    azi1 = models.IntegerField()
    # 站在目標小區,看源小區的方位角
    azi2 = models.IntegerField()

當增加related_name='from_cell_id'後,就可以這樣查:

ZJcell.objects.first().from_cell_id

這樣就知道是把ZJcell.objects.first()當做源小區,從鄰區表中提取資料,而不會提取鄰區表中ZJcell.objects.first()是目標小區的資料。

https://blog.csdn.net/meylovezn/article/details/46924893

 

 

(venv) C:\Users\Administrator\PycharmProjects\cellsmap>py manage.py makemigrations
You are trying to add a non-nullable field 'azi1' to zjadjacent without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit, and let me add a default in models.py
Select an option: 2

# 鄰區多對多關係表
class ZJadjacent(models.Model):
    cellfrom = models.ForeignKey(ZJcell, on_delete=models.CASCADE, related_name='from_cell_id')
    # 因為有兩個ZJcell外來鍵,必須對這兩個外來鍵命名為不同的名字,否則出現報錯fields.E304
    cellto = models.ForeignKey(ZJcell, on_delete=models.CASCADE, related_name='to_cell_id')
    # 兩個鄰區間的距離
    distance = models.IntegerField(default=None)
    # 站在源小區,看目標小區的方位角
    azi1 = models.IntegerField(default=None)
    # 站在目標小區,看源小區的方位角
    azi2 = models.IntegerField(default=None)