Django创建Command时自动生成CommandPositions报UnboundLocalError问题
问题
在Django项目中定义了如下模型:
from django.db import models from django.contrib.auth.models import User from django.templatetags.static import static from simple_history.models import HistoricalRecords from treebeard.mp_tree import MP_Node from . import constants from datetime import datetime class Profile(models.Model): # Managed fields user = models.OneToOneField(User, related_name="profile", on_delete=models.CASCADE) memberId = models.CharField(unique=True, max_length=15, null=False, blank=False, default=GenerateFA) avatar = models.ImageField(upload_to="static/MCARS/img/members", null=True, blank=True) birthday = models.DateField(null=True, blank=True) gender = models.CharField(max_length=10, choices=constants.GENDER_CHOICES, null=True, blank=True) invited = models.BooleanField(default=False) registered = models.BooleanField(default=False) height = models.PositiveSmallIntegerField(null=True, blank=True) phone = models.CharField(max_length=32, null=True, blank=True) address = models.CharField(max_length=255, null=True, blank=True) number = models.CharField(max_length=32, null=True, blank=True) city = models.CharField(max_length=50, null=True, blank=True) zip = models.CharField(max_length=30, null=True, blank=True) @property def get_avatar(self): return self.avatar.url if self.avatar else static('static/MCARS/img/avatars/default.jpg') def save(self, **kwargs): if not self.pk: super(Profile, self).save(**kwargs) rank = Rank.objects.create(user=self, longrank='o1', shortrank='o1', branch='r') rank.save() else: super(Profile, self).save(**kwargs) def __str__(self): rank = Rank.objects.get(user=self.user.profile).get_longrank_display() return rank + " " + self.user.first_name + " " + self.user.last_name + "(" + self.memberId + ")" class Rank(models.Model): user = models.ForeignKey(Profile, related_name="Rank", on_delete=models.CASCADE) longrank = models.CharField(max_length=5, null=True, blank=True, choices=constants.long_rank) shortrank = models.CharField(max_length=5, null=True, blank=True, choices=constants.short_rank) branch = models.CharField(max_length=5, null=True, blank=True, choices=constants.branch) image = models.ImageField(upload_to="static/MCARS/img/ranks", null=True, blank=True) history = HistoricalRecords() def save(self, **kwargs): self.shortrank = self.longrank self.image = 'static/MCARS/img/ranks/' + self.branch[0] + '-' + self.longrank + '.png' super(Rank, self).save(**kwargs) def __str__(self): return self.get_longrank_display() + ' (' + self.get_shortrank_display() + ') ' + self.user.user.first_name + ' ' + self.user.user.last_name class Command(MP_Node): CO = models.ForeignKey(User, related_name="user", on_delete=models.CASCADE) ship = models.ImageField(upload_to="static/MCARS/img/ships", null=True, blank=True) seal = models.ImageField(upload_to="static/MCARS/img/flag", null=True, blank=True) Type = models.CharField(max_length=10, choices=constants.CMD_TYPE) name = models.CharField(max_length=255, null=True, blank=True) address = models.CharField(max_length=255, null=True, blank=True) number = models.CharField(max_length=32, null=True, blank=True) city = models.CharField(max_length=50, null=True, blank=True) zip = models.CharField(max_length=30, null=True, blank=True) hull = models.CharField(max_length=255, null=True, blank=True) Commissioned = models.BooleanField(default=True) node_order_by = ['name'] def save(self, **kwargs): if not self.pk: super(Command, self).save(**kwargs) CommandPositions = CommandPositions.objects.create(command=self, name="CO", responsibility='Commanding Officer') CommandPositions.save() else: super(Command, self).save(**kwargs) def isCO(self, user): return self.CO == user def __str__(self): rank = Rank.objects.get(user=self.CO.profile).get_longrank_display() return self.name + ' (' + self.hull + ') ' + rank + " " + self.CO.first_name + " " + self.CO.last_name + " Commanding" class CommandPositions(models.Model): Command = models.ForeignKey(Command, related_name="Positions", on_delete=models.CASCADE) name = models.CharField(max_length=255, null=True, blank=True) responsibility = models.TextField(null=True, blank=True) class Assignment(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) Position = models.ForeignKey(Command, on_delete=models.CASCADE) assigned_dt = models.DateTimeField(auto_now_add=True) class Meta: verbose_name_plural = "Assignments"
创建Profile记录时能自动生成对应的Rank记录,但创建Command记录时,自动生成CommandPositions记录抛出如下错误:
File "/Users/evancutler/PycharmProjects/MCARS/MCARS/models.py", line 98, in save CommandPositions = CommandPositions.objects.create(command=self, name="CO", responsibility='Commanding Officer') ^^^^^^^^^^^^^^^^ Exception Type: UnboundLocalError at /admin/MCARS/command/add/ Exception Value: cannot access local variable 'CommandPositions' where it is not associated with a value
原因分析
问题出在Command模型的save方法里:你把局部变量名定义成了CommandPositions,和模型类名完全相同。Python解释器会优先把这个名字当作局部变量处理,当你执行CommandPositions.objects.create()时,局部变量CommandPositions还没有被赋值,就会触发UnboundLocalError。
而Profile模型的save方法里用的是rank作为变量名,和Rank模型类名不同,所以不会覆盖全局的模型类引用,因此可以正常调用Rank.objects.create()。
另外补充:objects.create()方法本身已经会自动保存新创建的对象,不需要再调用.save(),这部分是冗余代码。
解决方案
修改Command模型的save方法,把局部变量名改成和模型类不同的名字,同时去掉多余的.save()调用:
def save(self, **kwargs): if not self.pk: super(Command, self).save(**kwargs) # 变量名改为command_position,避免和模型类名冲突 command_position = CommandPositions.objects.create( command=self, name="CO", responsibility='Commanding Officer' ) else: super(Command, self).save(**kwargs)
或者更简洁的写法,直接调用create不赋值:
def save(self, **kwargs): if not self.pk: super(Command, self).save(**kwargs) CommandPositions.objects.create( command=self, name="CO", responsibility='Commanding Officer' ) else: super(Command, self).save(**kwargs)
这样就能解决变量名冲突导致的错误,同时简化代码。
内容的提问来源于stack exchange,提问作者arcee123
相关产品推荐
相关产品推荐

