Optional m2m relationships in Admin

54 views
Skip to first unread message

GeneralMean

unread,
Feb 4, 2009, 12:07:56 PM2/4/09
to Django users
This is a huge one:

I want to implement an optional m2m realtionship in django admin
application, so it is possible to create a new Game (model description
below) without choosing any tags associated with it. By default django
form validation returns an error when I try to do such thing. So I
created a new form (GamesAdminModelForm) and specified tag field as
"required=False". It works quite good, to the point when I want to add
some tags "by hand", after user submited the form. For example user
creates new Game, and after successful validation I want to add some
common tags to it.

The problem is, there is, none of the relations to newly created game
gets saved in DB. No matter what I do, I tried to save it in Game
model method save(), tried to do that in save_model() method of
GamesAdmin (of course after calling obj.save(). Code returns no
exception. I even tried to inject some tags to form in save_model
method, and then re-saving the form - didn't work out well.

The misterious thing of all, is that when I use function shown below,
everything works allright, to the point when I delete "assert Null"
command. What's with that?? When code throws exception m2m
relationships show up in database, when code DOESN'T throw exception
DB m2m relationship table remains empty.

def save_model(self, request, obj, form, change):
obj.save()
#get some tag
tag = Tag.objects.get(pk=1)
#add relationship to new object
obj.tag.add(tag)
assert Null

Hope someone gonna find the right answer.


Simplified models:

#in models.py
class Game(models.Model):
id = models.AutoField(primary_key=True, db_column='grg_id')
tags = models.ManyToManyField(Tag, related_name='g_tags')

class Tag(models.Model):
id = models.AutoField(primary_key=True, db_column='grt_id')
tag = models.CharField(max_length=255, unique=True,
db_column='grt_tag')
tag_url = models.CharField(max_length=255, blank=False,
db_column='grt_tag_url')
tag_keyword = models.CharField(max_length=255, blank=False,
db_column='grt_tag_keyword')

#in admin.py
class GamesAdmin(admin.ModelAdmin):
form = GamesAdminModelForm

admin.site.register(Game, GamesAdmin)
admin.site.register(Tag)

#forms.py
class GamesAdminModelForm(forms.ModelForm):
cover = forms.ImageField(widget=ImageWithThumbnailWidget,
required=False)
tag = forms.ModelMultipleChoiceField(queryset=Tags.objects.all
().order_by('tag'), required=False, widget=FilteredSelectMultiple
(verbose_name='Tagi', is_stacked=True))

class Meta:
model = Game

garagefan

unread,
Feb 4, 2009, 1:15:26 PM2/4/09
to Django users
Not totally sure this will help with your error, as i haven't come
across it yet.

But for fields that may be empty I also have a blank = True and null =
True. This is to tell the DB that it is acceptable to have no value in
the field.

ie, i'm using the following to choose add users to a private forum
category:

allowedUsers = models.ManyToManyField(User, verbose_name=_('Members'),
blank=True, null=True,
help_text=_('Select Users that are allowed to view this private
group'), limit_choices_to={'is_staff': False}
)

GeneralMean

unread,
Feb 4, 2009, 2:13:04 PM2/4/09
to Django users
Thanks for the answer,

I modified my Game model according to your suggestion, and also,
simplified whole thing.
I can create new game without having to choose any tags - that's
correct. Now, take a look at
the save_model() method in GamesAdmin - I try to associate newly
created Game with some
tag -- with no luck :/ Looks like django admin application prevents
from actual saving _any_ relationship
inside save_model in save() method of Game model as well - tested).
Any ideas how to force create of m2m relationship after the form got
submited, without using raw SQL query?

Current code:

#in models.py
class Game(models.Model):
id = models.AutoField(primary_key=True, db_column='grg_id')
tags = models.ManyToManyField(Tag, blank=True, null=True,
related_name='g_tags')

class Tag(models.Model):
id = models.AutoField(primary_key=True, db_column='grt_id')
tag = models.CharField(max_length=255, unique=True,
db_column='grt_tag')
tag_url = models.CharField(max_length=255, blank=False,
db_column='grt_tag_url')
tag_keyword = models.CharField(max_length=255, blank=False,
db_column='grt_tag_keyword')

#in admin.py
class GamesAdmin(admin.ModelAdmin):
#NOT using custom form anymore
#form = GamesAdminModelForm

def save_model(self, request, obj, form, change):
super(GamesAdmin, self).save_model(request, obj, form, change)
if not change:
#get a tag
tag = Tags.objects.get(pk=1)
#associate tag with newly created object
obj.tag.add(tag)

admin.site.register(Game)
admin.site.register(Tag)

garagefan

unread,
Feb 4, 2009, 2:24:58 PM2/4/09
to Django users
From my limited understanding of Django...

the admin.py stuff should not contain any save function.

http://docs.djangoproject.com/en/dev/ref/contrib/admin/#ref-contrib-admin

what exactly are you trying to do with the tag? Your M2M field in the
initial Game class creates the relationship.

You may want to looking into using the django-tagging app, which is
what i used for my stupid little blog app (doesn't everyone create
that first? lol )

If it helps, the first thing i did when learning django is follow the
webmonkey.com django tutorial http://www.webmonkey.com/tutorial/Install_Django_and_Build_Your_First_App

i found this infinitely more useful than the tutorial on
djangoproject.com, as it goes into things a bit more complex and gives
a greater idea on how things work together in the framework.

Also, don't underestimate the framework... it does a lot of things for
you, which is the point. It allows you to build an app quickly without
worrying about the annoyances of SQL.

koenb

unread,
Feb 4, 2009, 2:52:59 PM2/4/09
to Django users
>     def save_model(self, request, obj, form, change):
>         super(GamesAdmin, self).save_model(request, obj, form, change)
>         if not change:
>             #get a tag
>             tag = Tags.objects.get(pk=1)
>             #associate tag with newly created object
>             obj.tag.add(tag)
>

Shouldn't this be obj.tags.add(tag) ? (your m2mfield is called tags
not tag)

GeneralMean

unread,
Feb 4, 2009, 2:54:59 PM2/4/09
to Django users
On 4 Lut, 20:24, garagefan <monkeygar...@gmail.com> wrote:
> From my limited understanding of Django...
>
> the admin.py stuff should not contain any save function.
>
> http://docs.djangoproject.com/en/dev/ref/contrib/admin/#ref-contrib-a...

Hmm, take a look at this then:
http://docs.djangoproject.com/en/dev/ref/contrib/admin/#save-model-self-request-obj-form-change

From examples from around the net, this method can be used for
modyfing your object model before actual save in DB. For example
inserting currently logged in user, as author of a blog post or so
(using request object passed to save_model method).

> what exactly are you trying to do with the tag? Your M2M field in the
> initial Game class creates the relationship.

I'm trying to automatically create tag based on new Game name, and
then
associated it with that specific game. In theory save_model() or save
() method in Game model is the best place to do that.
Unfortunately, something is wrong, either with my logic or django :)

> You may want to looking into using the django-tagging app, which is
> what i used for my stupid little blog app (doesn't everyone create
> that first? lol )
>
> If it helps, the first thing i did when learning django is follow the
> webmonkey.com django tutorialhttp://www.webmonkey.com/tutorial/Install_Django_and_Build_Your_First...
>
> i found this infinitely more useful than the tutorial on
> djangoproject.com, as it goes into things a bit more complex and gives
> a greater idea on how things work together in the framework.
>
> Also, don't underestimate the framework... it does a lot of things for
> you, which is the point. It allows you to build an app quickly without
> worrying about the annoyances of SQL.

Exactly - it does a lot of things, and the last thing I'd like to do
is reverting to raw SQL. Actually I've spent whole day trying to
figure out how to take advantage of Django "engine" instead of
inserting rows manually.

GeneralMean

unread,
Feb 4, 2009, 3:04:22 PM2/4/09
to Django users
>On 4 Lut, 20:52, koenb <koen.bierm...@werk.belgie.be> wrote:
> Shouldn't this be obj.tags.add(tag) ? (your m2mfield is called tags
> not tag)

Typo... It actually should say obj.tag.add(tag) - I wasn't pasting
this code, wrote it by hand ;)
Actual code has no syntax errors :)

garagefan

unread,
Feb 4, 2009, 3:25:21 PM2/4/09
to Django users
check out django-tagging. It looks like they're using a custom Manager
that is handling the creation of new tags based off of an object that
is using "tags" listing new tags.

GeneralMean

unread,
Feb 4, 2009, 4:13:48 PM2/4/09
to Django users
On 4 Lut, 21:25, garagefan <monkeygar...@gmail.com> wrote:
> check out django-tagging. It looks like they're using a custom Manager
> that is handling the creation of new tags based off of an object that
> is using "tags" listing new tags.

Will do.
Meantime, let's take a look again at the weirdest thing:

this DOES work (m2m relationship appear in DB):

class GamesAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
super(GamesAdmin, self).save_model(request, obj, form, change)
if not change:
tag = Tags.objects.get(pk=2)
obj.tag.add(tag)
#the best part
assert null

this DOESN'T work (m2m relationship doesn't appear in DB):

class GamesAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
super(GamesAdmin, self).save_model(request, obj, form, change)
if not change:
tag = Tags.objects.get(pk=2)
obj.tag.add(tag)
#the best part - not asserting anything
#assert null

What's going on behind the scene? I'm runnin' out of ideas, please,
help.
Reply all
Reply to author
Forward
0 new messages