解决Django添加Message时主键值缺失报错问题
Django后台添加Message时报错:‘Message' instance needs to have a primary key value before this relationship can be used
问题背景
在Django后台创建Message实例时触发上述错误,已尝试以下操作但未解决:
- 添加了自定义主键字段
id = models.BigAutoField(primary_key=True, auto_created=True) - 验证SQLite数据库中主键自增结构正常
- 参考过Stack Overflow和GitHub上的常规修复方案
怀疑是Django/Python版本更新导致的兼容性问题,考虑修改Message与Article的关联方式,但因是Jazzband Newsletter的原生设计,暂时犹豫是否调整。
相关代码
models.py中的Message类
class Message(models.Model): """ Message as sent through a Submission. """ default_id = time.time() #adding primary key to satisfy error id = models.BigAutoField(primary_key=True, auto_created=True) title = models.CharField(max_length=200, verbose_name=_('title')) slug = models.SlugField(verbose_name=_('slug')) newsletter = models.ForeignKey( Newsletter, verbose_name=_('newsletter'), on_delete=models.CASCADE, default=get_default_newsletter ) date_create = models.DateTimeField( verbose_name=_('created'), auto_now_add=True, editable=False ) date_modify = models.DateTimeField( verbose_name=_('modified'), auto_now=True, editable=False ) class Meta: verbose_name = _('message') verbose_name_plural = _('messages') unique_together = ('slug', 'newsletter') #order_with_respect_to = 'newsletter' def __str__(self): try: return _("%(title)s in %(newsletter)s") % { 'title': self.title, 'newsletter': self.newsletter } except Newsletter.DoesNotExist: logger.warning('No newsletter has been set for this message yet.') return self.title def get_next_article_sortorder(self): """ Get next available sortorder for Article. """ next_order = self.articles.aggregate( models.Max('sortorder') )['sortorder__max'] if next_order: return next_order + 10 else: return 10 @cached_property def _templates(self): """Return a (subject_template, text_template, html_template) tuple.""" return self.newsletter.get_templates('message') @property def subject_template(self): return self._templates[0] @property def text_template(self): return self._templates[1] @property def html_template(self): return self._templates[2] @classmethod def get_default(cls): try: return cls.objects.order_by('-date_create').all()[0] except IndexError: return None
admin.py中的MessageAdmin类
class MessageAdmin(NewsletterAdminLinkMixin, ExtendibleModelAdminMixin, admin.ModelAdmin): logger.critical('MessageAdmin') save_as = True list_display = ( 'admin_title', 'admin_newsletter', 'admin_preview', 'date_create', 'date_modify' ) list_filter = ('newsletter', ) date_hierarchy = 'date_create' prepopulated_fields = {'slug': ('title',)} inlines = [ArticleInline, AttachmentInline, ] """ List extensions """ def admin_title(self, obj): return format_html('<a href="{}/">{}</a>', obj.id, obj.title) admin_title.short_description = _('message') def admin_preview(self, obj): url = reverse('admin:' + self._view_name('preview'), args=(obj.id,), current_app=self.admin_site.name) return format_html('<a href="{}">{}</a>', url, _("Preview")) admin_preview.short_description = '' """ Views """ def preview(self, request, object_id): logger.critical('preview object_id = %s', object_id) return render( request, "admin/newsletter/message/preview.html", {'message': self._getobj(request, object_id), 'attachments': Attachment.objects.filter(message_id=object_id)}, ) @xframe_options_sameorigin def preview_html(self, request, object_id): logger.critical('preview_html object_id = %s', object_id) message = self._getobj(request, object_id) if not message.html_template: raise Http404(_( 'No HTML template associated with the newsletter this ' 'message belongs to.' )) c = { 'message': message, 'site': Site.objects.get_current(), 'newsletter': message.newsletter, 'date': now(), 'STATIC_URL': settings.STATIC_URL, 'MEDIA_URL': settings.MEDIA_URL } return HttpResponse(message.html_template.render(c)) @xframe_options_sameorigin def preview_text(self, request, object_id): message = self._getobj(request, object_id) c = { 'message': message, 'site': Site.objects.get_current(), 'newsletter': message.newsletter, 'date': now(), 'STATIC_URL': settings.STATIC_URL, 'MEDIA_URL': settings.MEDIA_URL } return HttpResponse( message.text_template.render(c), content_type='text/plain' ) def submit(self, request, object_id): submission = Submission.from_message(self._getobj(request, object_id)) change_url = reverse( 'admin:newsletter_submission_change', args=[submission.id]) return HttpResponseRedirect(change_url) def subscribers_json(self, request, object_id): message = self._getobj(request, object_id) json = serializers.serialize( "json", message.newsletter.get_subscriptions(), fields=() ) return HttpResponse(json, content_type='application/json') """ URLs """ def get_urls(self): urls = super().get_urls() my_urls = [ path('<object_id>/preview/', self._wrap(self.preview), name=self._view_name('preview')), path('<object_id>/preview/html/', self._wrap(self.preview_html), name=self._view_name('preview_html')), path('<object_id>/preview/text/', self._wrap(self.preview_text), name=self._view_name('preview_text')), path('<object_id>/submit/', self._wrap(self.submit), name=self._view_name('submit')), path('<object_id>/subscribers/json/', self._wrap(self.subscribers_json), name=self._view_name('subscribers_json')), ] return my_urls + urls
修复方案
1. 修复get_next_article_sortorder方法
错误核心是:Message实例未保存(无主键)时,该方法就尝试查询关联的articles集合。修改方法,先判断实例是否已保存:
def get_next_article_sortorder(self): """ Get next available sortorder for Article. """ # 实例未保存时直接返回默认排序值 if not self.pk: return 10 next_order = self.articles.aggregate( models.Max('sortorder') )['sortorder__max'] if next_order: return next_order + 10 else: return 10
2. 调整Admin Inline配置
检查ArticleInline的extra参数,避免默认生成过多空表单触发关联查询:
class ArticleInline(admin.TabularInline): model = Article extra = 0 # 减少默认空表单数量,避免提前触发关联逻辑
3. 移除自定义主键(可选)
Django默认会自动生成自增主键id,手动添加的BigAutoField可能和默认逻辑冲突,尝试移除该字段后重新执行迁移:
# 删除models.py中的这一行 # id = models.BigAutoField(primary_key=True, auto_created=True)
4. 验证版本兼容性
检查当前Django版本与Jazzband Newsletter的兼容情况,优先升级Jazzband Newsletter到对应Django版本的最新稳定版,避免版本差异引发的隐性bug。
内容的提问来源于stack exchange,提问作者Ali Vleotsky
相关产品推荐
相关产品推荐

