1

多対多の関係を持つモデルがあります。pre_saveシグナルでは、インスタンス データを前処理したい:

@receiver(pre_save, sender=Book)
def pre_save_tags(sender, instance, **kwargs):
    # tags is a many-to-many relation
    print instance.tags

tagsフィールドに設定されSelectMultipleます。したがって、複数の値があります。

問題

ValueError: 'Book' instance needs to have a primary key value before a many-to-many relationship can be used.
  1. まだ保存されていないインスタンスのタグを取得するにはどうすればよいですか?
  2. 保存する前にタグを変更するにはどうすればよいですか?

に限りませんpre_save。より良い解決策があれば、私はすべての耳です。

4

2 に答える 2

0

とった。もっとエレガントな方法があると確信しています。コードを改善する方法を知っている場合は、私にいくつかの指針を与えるか、より良い解決策を提供してください。

class ManyToManyTagField(ManyToManyField):

    def formfield(self, **kwargs):
        defaults = {
            'form_class': TagField,
        }
        return super(ManyToManyTagField, self).formfield(**defaults)

    def save_form_data(self, instance, data):
        tags = []
        for tag_id in data:
            autocompletedTag = tag_id.find('___')
            if autocompletedTag > 0:
                parts = tag_id.split('___')
                tags.append(parts[0])
            else:
                newTag = Tag(name=tag_id, category=TagCategory.objects.get(name='New'))
                newTag.save()
                tags.append(newTag.id)

        return super(ManyToManyTagField, self).save_form_data(instance, tags)

関連する部分はメソッドsave_form_dataです。

完全を期すために、他のクラスを次に示します。

タグウィジェット:

class TagWidget(SelectMultiple):
    """
    Widget for Tag input fields.
    """
    queryset = []

    def render(self, name, value, attrs=None):
        final_attrs = self.build_attrs(attrs, name=name)
        final_attrs['class'] = 'tag-input'

        selectedTags = []
        if value:
            for tag_id in value:
                if isinstance(tag_id, (int, long)) or tag_id.isdigit():
                    tag_name = Tag.objects.get(pk=tag_id).name
                    selectedTags.append({ 'value': str(tag_id) + '___' + tag_name, 'label': tag_name })
                else:
                    autocompletedTag = tag_id.find('___')
                    if autocompletedTag > 0:
                        parts = tag_id.split('___')
                        selectedTags.append({ 'value': parts[0] + '___' + parts[1], 'label': parts[1] })
                    else:
                        selectedTags.append({ 'value': tag_id, 'label': tag_id })

        allTags = []
        for tag in self.queryset:
            allTags.append({ 'value': str(tag.id) + '___' + str(tag.name), 'label': str(tag.name) });

        allTagsJson = simplejson.dumps(allTags)

        return render_to_string('form/tagwidget.html',
                                {'attrs': flatatt(final_attrs), 'selectedTags': selectedTags, 'allTags': allTagsJson})

タグフィールド:

class TagField(ModelMultipleChoiceField):
    widget = TagWidget

    def __init__(self, queryset, *args, **kwargs):
        self.widget.queryset = queryset
        super(TagField, self).__init__(queryset, *args, **kwargs)

    def clean(self, value):
        if self.required and not value:
            raise ValidationError(self.error_messages['required'])
        elif not self.required and not value:
            return []
        if not isinstance(value, (list, tuple)):
            raise ValidationError(self.error_messages['list'])
        # Since this overrides the inherited ModelChoiceField.clean
        # we run custom validators here
        self.run_validators(value)
        return value

タグウィジェット.html:

{% autoescape off %}
<ul{{ attrs }}>
{% for tag in selectedTags %}
    <li tagValue="{{ tag.value }}">{{ tag.label }}</li>
{% endfor %}
</ul>
<script type="text/javascript">
var autocompletionData = {{ allTags }};
</script>
{% endautoescape %}
于 2012-05-12T23:17:06.053 に答える
0

これは、オブジェクトが新しく、まだ保存されていないために発生します。そこには主キーがありません。以前に保存されたオブジェクトを保存すると、このエラーは発生しません。

post_save を使用しないのはなぜですか。post_save では、オブジェクトが保存されているため、主キーがあります。pre_save を使用する必要がある場合は、instance.id が存在するかどうかを確認できます。存在しない場合、タグを持つことはできません (ManyToMany-fields が機能するには、オブジェクトにプライマリ ID が必要であるため (したがって例外))

主キーをテストするには、次のようにします。

@receiver(pre_save, sender=Book)
def pre_save_tags(sender, instance, **kwargs):
    # tags is a many-to-many relation
    if instance.id:
        print instance.tags
于 2012-05-12T22:16:06.033 に答える