如何使_meta.local_fields与数据库中的表模式不匹配?

0 投票
1 回答
635 浏览
提问于 2025-04-15 18:58

我完全搞不懂为什么 _meta.local_fields 返回的字段比数据库表里的字段还要多。User 模型是从 contrib.auth.models.User 继承过来的。

$ mysql -u user -p database  
Enter password:   
Reading table information for completion of table and column names  
You can turn off this feature to get a quicker startup with -A  

Welcome to the MySQL monitor.  Commands end with ; or \g.  
Your MySQL connection id is 1240032  
Server version: 5.0.77 Source distribution  

mysql> describe auth_user;  
+--------------------+------------------+------+-----+---------+----------------+  
| Field              | Type             | Null | Key | Default | Extra          |  
+--------------------+------------------+------+-----+---------+----------------+  
| id                 | int(11)          | NO   | PRI | NULL    | auto_increment |   
| username           | varchar(30)      | NO   | UNI | NULL    |                |   
| first_name         | varchar(30)      | NO   |     | NULL    |                |   
| last_name          | varchar(30)      | NO   |     | NULL    |                |   
| email              | varchar(75)      | NO   |     | NULL    |                |   
| password           | varchar(128)     | NO   |     | NULL    |                |   
| is_staff           | tinyint(1)       | NO   |     | NULL    |                |   
| is_active          | tinyint(1)       | NO   |     | NULL    |                |   
| is_superuser       | tinyint(1)       | NO   |     | NULL    |                |   
| last_login         | datetime         | NO   |     | NULL    |                |   
| date_joined        | datetime         | NO   |     | NULL    |                |   
| email_isvalid      | tinyint(1)       | NO   |     | NULL    |                |   
| email_key          | varchar(16)      | YES  |     | NULL    |                |   
| reputation         | int(10) unsigned | NO   |     | NULL    |                |   
| gravatar           | varchar(32)      | NO   |     | NULL    |                |   
| gold               | smallint(6)      | NO   |     | NULL    |                |   
| silver             | smallint(6)      | NO   |     | NULL    |                |   
| bronze             | smallint(6)      | NO   |     | NULL    |                |   
| questions_per_page | smallint(6)      | NO   |     | NULL    |                |   
| last_seen          | datetime         | NO   |     | NULL    |                |   
| real_name          | varchar(100)     | NO   |     | NULL    |                |   
| website            | varchar(200)     | NO   |     | NULL    |                |   
| location           | varchar(100)     | NO   |     | NULL    |                |   
| date_of_birth      | date             | YES  |     | NULL    |                |   
| about              | longtext         | NO   |     | NULL    |                |   
+--------------------+------------------+------+-----+---------+----------------+  
25 rows in set (0.00 sec)  

从 Django 的错误页面来看,生成错误的 SQL 语句是:异常值:(1110, "Column 'about' specified twice")

'INSERT INTO `auth_user` (`username`, `first_name`, `last_name`, `email`, `password`, `is_staff`, `is_active`, `is_superuser`, `last_login`, `date_joined`,     `email_isvalid`, `email_key`, `reputation`, `gravatar`, `gold`, `silver`, `bronze`, `questions_per_page`, `last_seen`, `real_name`, `website`, `location`, `date_of_birth`, `about`, `email_isvalid`, `email_key`, `reputation`, `gravatar`, `gold`, `silver`, `bronze`, `questions_per_page`, `last_seen`, `real_name`, `website`, `location`, `date_of_birth`, `about`) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)'     

这个 SQL 语句似乎是通过遍历 User._meta.local_fields 生成的。
我不明白为什么 _meta.local_fields 和实际的 User 表结构不一致。

$ python2.5 manage.py shell  
Python 2.5.4 (r254:67916, Aug  5 2009, 12:42:40)   
[GCC 4.1.2 20080704 (Red Hat 4.1.2-44)] on linux2  
Type "help", "copyright", "credits" or "license" for more information.  
(InteractiveConsole)  
>>> from forum.models import User  
>>> len(User._meta.local_fields)  
39  
>>> import pprint  
>>> pprint.pprint(User._meta.local_fields)  
[<django.db.models.fields.AutoField object at 0x852c80c>,  
 <django.db.models.fields.CharField object at 0x8528c4c>,  
 <django.db.models.fields.CharField object at 0x8528cac>,  
 <django.db.models.fields.CharField object at 0x8528d0c>,  
 <django.db.models.fields.EmailField object at 0x8528d6c>,  
 <django.db.models.fields.CharField object at 0x8528e2c>,  
 <django.db.models.fields.BooleanField object at 0x8528ecc>,  
 <django.db.models.fields.BooleanField object at 0x8528f6c>,  
 <django.db.models.fields.BooleanField object at 0x852c02c>,  
 <django.db.models.fields.DateTimeField object at 0x852c0ac>,  
 <django.db.models.fields.DateTimeField object at 0x852c0ec>,  
 # here is where customizations to User begin.
 <django.db.models.fields.BooleanField object at 0x861744c>,  
 <django.db.models.fields.CharField object at 0x861732c>,  
 <django.db.models.fields.PositiveIntegerField object at 0x861746c>,  
 <django.db.models.fields.CharField object at 0x861748c>,  
 <django.db.models.fields.SmallIntegerField object at 0x861784c>,  
 <django.db.models.fields.SmallIntegerField object at 0x86178ec>,  
 <django.db.models.fields.SmallIntegerField object at 0x861792c>,  
 <django.db.models.fields.SmallIntegerField object at 0x861796c>,  
 <django.db.models.fields.DateTimeField object at 0x861798c>,  
 <django.db.models.fields.CharField object at 0x86179cc>,  
 <django.db.models.fields.URLField object at 0x8617a0c>,  
 <django.db.models.fields.CharField object at 0x8617a4c>,  
 <django.db.models.fields.DateField object at 0x8617a8c>,  
 <django.db.models.fields.TextField object at 0x8617acc>,  
 # this seems to be a duplicate of the fields added to User
 <django.db.models.fields.BooleanField object at 0x862ab2c>,  
 <django.db.models.fields.CharField object at 0x862a4ac>,  
 <django.db.models.fields.PositiveIntegerField object at 0x862ab6c>,  
 <django.db.models.fields.CharField object at 0x862f6cc>,  
 <django.db.models.fields.SmallIntegerField object at 0x861782c>,  
 <django.db.models.fields.SmallIntegerField object at 0x862fa2c>,  
 <django.db.models.fields.SmallIntegerField object at 0x862fa4c>,  
 <django.db.models.fields.SmallIntegerField object at 0x862fa8c>,  
 <django.db.models.fields.DateTimeField object at 0x862faac>,  
 <django.db.models.fields.CharField object at 0x862faec>,  
 <django.db.models.fields.URLField object at 0x862fb2c>,  
 <django.db.models.fields.CharField object at 0x862fb6c>,  
 <django.db.models.fields.DateField object at 0x862fbac>,  
 <django.db.models.fields.TextField object at 0x862fbec>]  

模型中添加的额外字段是这样加上的:

User.add_to_class('email_isvalid', models.BooleanField(default=False))  
User.add_to_class('email_key', models.CharField(max_length=16, null=True))  
User.add_to_class('reputation', models.PositiveIntegerField(default=1))  
User.add_to_class('gravatar', models.CharField(max_length=32))  
User.add_to_class('email_feeds', generic.GenericRelation(EmailFeed))  
User.add_to_class('favorite_questions', models.ManyToManyField(Question, through=FavoriteQuestion, related_name='favorited_by'))  
User.add_to_class('badges', models.ManyToManyField(Badge, through=Award, related_name='awarded_to'))  
User.add_to_class('gold', models.SmallIntegerField(default=0))  
User.add_to_class('silver', models.SmallIntegerField(default=0))  
User.add_to_class('bronze', models.SmallIntegerField(default=0))  
User.add_to_class('questions_per_page', models.SmallIntegerField(choices=QUESTIONS_PER_PAGE_CHOICES, default=10))  
User.add_to_class('last_seen', models.DateTimeField(default=datetime.datetime.now))  
User.add_to_class('real_name', models.CharField(max_length=100, blank=True))  
User.add_to_class('website', models.URLField(max_length=200, blank=True))  
User.add_to_class('location', models.CharField(max_length=100, blank=True))  
User.add_to_class('date_of_birth', models.DateField(null=True, blank=True))  
User.add_to_class('about', models.TextField(blank=True))  

我觉得 .add_to_class 方法可能和这个问题有关。

1 个回答

0

如果你能展示一下和这个问题相关的 Django 模型,那就太好了。从你 SQL 里的字段名来看,这个模型似乎是从 contrib.auth.models.User 继承的,是这样吗?如果是的话,你有没有可能重复定义了 User 模型里的某个字段名?

更新:更直接一点,我是想让你编辑一下你的问题,并且包含你在 models.py 文件中声明的具体模型。几乎可以肯定你定义了两个同名的字段。在 auth.models.User 中最后一个定义的字段是 date_joined,所以 的模型声明中应该有 about 的两个定义。

实际上,再仔细看看你的 SQL,你不仅有两个名为 about 的列,还有大约 14 个重复的字段名。这样可不太对劲。

用 Jerry McGuire 的话说:给我看看模型! :-)

第二次更新(喝了一杯啤酒,心情不错!)

我从来没有这样使用过 add_to_class。你为什么选择这种方式,而不是用一些更标准的 Django 模型继承 技巧呢?或者,既然你在处理的是 User,为什么不使用 UserProfile 呢?虽然有点麻烦,但其实也能正常工作。

第三次更新

啊,Domini Nabisco,我的孩子——我没意识到你是从别人那里继承过来的。提醒你一下:因为这改变了 Django 中的一个基本类(而且是以一种明显不标准的方式),你可能需要检查所有 user.py 文件和模板中的引用。根据继承项目的大小,这可能会非常麻烦,尤其是当缺少测试时。你有没有可能找到那个负责的人,获取更多的见解?这样可能是让事情正常工作的更快捷的方式。祝你好运!

撰写回答