Form/view for ManyToMany relationship

2011-03-03 Thread kgardenia42
Hi list,

Considering this schema (below) I'm trying to figure out how to create
a ModelForm which links Actors to Films (all known actors in a multi-
select widget).  I can't seem to figure out how to do this in a way
that ModelForm will do the heavy lifting.

I would like to make a page (e.g. /film/add_actors/123) that has a
multi select widget which allows me to select one or more Actors (from
a list of all possible actors) and link them to the given film object.

Is ModelForm the right thing to use here?

I'm going to outline what I've tried to give context.

If I simply do this:

class FilmRoleForm(forms.ModelForm):
class Meta:
model = FilmRole
fields = (('actor'),)

Then it only gives me a form to populate one row in FilmRole (I know I
have to manually munge the other fields but I can handle that).

That behaviour makes sense.   However, it isn't the behaviour I want.
I want to be able to populate many rows at once (since a film can have
many actors).

So I tried this:

class FilmRoleForm(forms.ModelForm):
actor = forms.ModelMultipleChoiceField(
widget=forms.SelectMultiple,
queryset = Actor.objects.all()
)

class Meta:
model = FilmRole
fields = (('actor'),)

ok - progress.  Now I get the widget I want (a multi select list) but
when I submit it I get an exception like this:

Cannot assign "[, ]":
"FilmRole.actor" must be a "Actor" instance.

Again, I can see why this is but it isn't the behaviour I want.

How can I tell Django "use a multi-select widget and I expect a list
of values not a single (non list) value".

Assuming I were able to circumvent that problem, I'm also trying to
avoid having to write code like this in the view:

for actor in form.cleaned_data.get('actor'):
FilmRole.get_or_create(actor = actor, film = film)

... that isn't so bad but it also requires that I keep track of rows I
need to delete (those FilmRole objects in the database from previous
submits that have been unselected in the latest
form.cleaned_data.get('actor'))

I'd just like all that behaviour be implicit somehow.  I know that
when my mapping table (FilmRole) is implicitly created and doesn't
have extract fields then Django can handle all that magically.   Is
there some other variant of ModelForm that can handle this?

Any suggestions are most welcome.

Thanks.

#


class Actor(models.Model):
name = models.CharField(max_length=255)
dob = models.DateField()

def __unicode__(self):
return self.name

class Film(models.Model):
title = models.CharField(max_length=255)
actors = models.ManyToManyField(Actor, through = 'FilmRole')
released = models.DateField()

def __unicode__(self):
return self.title

class FilmRole(models.Model):
film = models.ForeignKey(Film)
actor = models.ForeignKey(Actor)
date_joined = models.DateField()
earnings = models.IntegerField()

class Meta:
unique_together = ("film", "actor")

-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To post to this group, send email to django-users@googlegroups.com.
To unsubscribe from this group, send email to 
django-users+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-users?hl=en.



Re: Form/view for ManyToMany relationship

2011-03-03 Thread kgardenia42
On Thu, Mar 3, 2011 at 11:50 AM, werefr0g  wrote:
> Hello,
>
> Sorry if I misunderstand, but what is wrong about using a ModelForm on your
> Film bounded to the film instance?

When I tried that I kept running into this error:
  "Cannot set values on a ManyToManyField which specifies an
intermediary model.  Use foo.FilmRole's Manager instead."

... which led me down a different path but perhaps I should have
persevered more.

I have googled this error extensively and spent some time digging into
Django internals to figure out what is happening.  Basically I can see
that if Django didn't auto-generate the relationship manager class
then it will throw this error.

Conceptually I totally get why that is (it has no way of knowing what
values to assign to the extra fields).  What I don't get is how I'm
supposed to get around it.  Lets say I wanted to put some code
somewhere to munge the missing fields to some default value.  Where
would I put that code?

I can't relate to the error message at all.  I read about manager
classes and how I can override query sets and so on.  Sure that
totally makes sense.  But how does that help me here?How can I use
the manager for FilmRole?  Do I need to override some method in my
manager?  Which method?   An example would be really useful here.

Thanks.

-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To post to this group, send email to django-users@googlegroups.com.
To unsubscribe from this group, send email to 
django-users+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-users?hl=en.



Re: Form/view for ManyToMany relationship

2011-03-04 Thread kgardenia42
On Fri, Mar 4, 2011 at 1:08 PM, werefr0g  wrote:
> On Thu, Mar 3, 2011 at 11:50 AM, werefr0g  wrote:
>
> Hello,
>
> Sorry if I misunderstand, but what is wrong about using a ModelForm on your
> Film bounded to the film instance?
>
> My bad... how did I missed the intermediary model involved in the ManyToMany
> relashionship. Sorry.
>
> When I tried that I kept running into this error:
>   "Cannot set values on a ManyToManyField which specifies an
> intermediary model.  Use foo.FilmRole's Manager instead.
>
> As you are using an intermediary model, you must insert multiple FilmRole,
> one for each actor to this film, but you don't want using a formset, do
> you?.
>
> Is the following going toward your goal?
>
> # forms.py
> class FilmActorsForm(forms.Form):
>     actors = forms.ModelMultipleChoiceField(queryset=Actors...,
> required=...)
>
> # views.py
> def assign_actors(film_id):
>     film = get_object_or_404(Film, id=film_id)
>     if request.method == 'POST':
>     form = FilmActorsForm(request.POST)
>     if form.is_valid():
>     chosen_actors = form.cleaned_data['actors']
>     current_actors = film.actors.all()
>     # handle deletion / creation of filmroles
>     return HttpResponseRedirect(...)
>     else:
>     form = FilmActorForm()

Thanks.  This is pretty close to what I'm doing.  However, I'd like to
not have to write all the associated boiler-plate (i.e. I have to
figure out if any film/actor relationships have been deleted since the
last save and explicitly write code to remove those and so on).

It seems that I would even have this problem if I added a simple "auto
now" date/time field to my relationship class, which seems a bit
restrictive.

ModelForm already knows how to do all that (it can do so when the
relationship class is implicitly created) so what I imagined was
*somewhere* I could override a method and provide the defaults for the
missing fields and then it would all  just works magically.Does
this make sense?  Can anyone give me any pointers to how I might do
this (I'm keen to learn the "right way" to do things).

I'm happy to hack this myself and contribute something if someone
could give me some pointers of where I should start or where something
like this ought to live.

Thanks.

-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To post to this group, send email to django-users@googlegroups.com.
To unsubscribe from this group, send email to 
django-users+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-users?hl=en.



ManyToMany without through table

2016-11-18 Thread 'kgardenia42' via Django users
Lets say I have a User who I want to allow to have many different
UserProfile objects (for multi-tenancy).

class User(models.Model):
# other user fields ...

profiles = models.ManyToManyField('UserProfile',
related_name='profiles', blank=True, null=True)

class UserProfile(models.Model):
user = models.ForeignKey('User', related_name='profile_user')
# other profile fields ...

This is fine but it will create a mapping table which for reasons I'll
explain later it creates a problematic layer of indirection between User
and UserProfile.  i.e. I'd like UserProfile be a through table for itself.

the reason this is problematic for me is that removing the mapping table
would make it much simpler in the admin.  for example lets say I have this
admin:

class UserProfileInline(admin.StackedInline):
model = UserProfile
extra = 0

class UserAdmin(admin.ModelAdmin):
inlines = [UserProfileInline]

That provides the ideal user interface in the sense that I have a nice
inline edtitor for each of the fields in a UserProfile which hangs off the
User (in a 1-to-m).

However, it is actually wrong because it creates rows in the UserProfile
table directly and misses out the implicit mapping (aka "through") table.

I am aware that the correct way to do this is:

class UserProfileInline(admin.StackedInline):
model = User.profiles.through
extra = 0

However, this results in a non-ideal user-interface in the sense that I now
get a dropdown of all the existing mapping rows without any of the nice
inline create/edit for the UserProfile fields.

So to sum up:
1] is there a way in this case to just skip out the mapping table and just
have UserProfile be both the m2m table *and* the through table?

OR

2] is there a way to write the admin such that the "through" table expands
to an inline editor for the table the through targets rather than just the
list of mappings in the through table?

PS - in relation to #1 I came up with this whacky idea:

class User(models.Model):
# other user fields ...

profiles = models.ManyToManyField('UserProfile',
  related_name='profiles',
  through='UserProfile',
  through_fields=('user', 'self_id'),
  blank=True, null=True)


class UserProfile(models.Model):
self_id = models.ForeignKey('self')
user = models.ForeignKey('User', related_name='profile_user')
# other profile fields ...

This seems like a potentially viable hack to accomplish what I'm
discussing.  i.e. User now "has many" UserProfile without any need for an
intermediate mapping table.  meaning I can get the ideal admin interface.

the rub is that I'd have to insert each UserProfile and then UPDATE the
self_id to have the same value as "id".

if through_fields allowed "id" I wouldn't have this problem.  but in my
Django version at least it doesn't allow that.  is there any way around
this?

Thanks for reading.

-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-users+unsubscr...@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-users/CAP2id1D974GhKv-HwbpAOGfzcWWi5r%3D4A4MrH4pOLP8sZ6qCtw%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.


ManyToMany without through table

2016-11-18 Thread 'kgardenia42' via Django users
Lets say I have a User who I want to allow to have many different 
UserProfile objects (for multi-tenancy).

class User(models.Model):
# other user fields ... 

profiles = models.ManyToManyField('UserProfile', 
related_name='profiles', blank=True, null=True)

class UserProfile(models.Model):
user = models.ForeignKey('User', related_name='profile_user')
# other profile fields ...

This is fine but it will create a mapping table which for reasons I'll 
explain later it creates a problematic layer of indirection between User 
and UserProfile.  i.e. I'd like UserProfile be a through table for itself.

the reason this is problematic for me is that removing the mapping table 
would make it much simpler in the admin.  for example lets say I have this 
admin:

class UserProfileInline(admin.StackedInline):
model = UserProfile
extra = 0

class UserAdmin(admin.ModelAdmin):
inlines = [UserProfileInline]

That provides the ideal user interface in the sense that I have a nice 
inline edtitor for each of the fields in a UserProfile which hangs off the 
User (in a 1-to-m).

However, it is actually wrong because it creates rows in the UserProfile 
table directly and misses out the implicit mapping (aka "through") table.

I am aware that the correct way to do this is:

class UserProfileInline(admin.StackedInline):
model = User.profiles.through
extra = 0

However, this results in a non-ideal user-interface in the sense that I now 
get a dropdown of all the existing mapping rows without any of the nice 
inline create/edit for the UserProfile fields.

So to sum up:
1] is there a way in this case to just skip out the mapping table and just 
have UserProfile be both the m2m table *and* the through table?

OR

2] is there a way to write the admin such that the "through" table expands 
to an inline editor for the table the through targets rather than just the 
list of mappings in the through table?

PS - in relation to #1 I came up with this whacky idea:

class User(models.Model):
# other user fields ... 

profiles = models.ManyToManyField('UserProfile',
  related_name='profiles',
  through='UserProfile',
  through_fields=('user', 'self_id'),
  blank=True, null=True)


class UserProfile(models.Model):
self_id = models.ForeignKey('self')
user = models.ForeignKey('User', related_name='profile_user')
# other profile fields ...

This seems like a potentially viable hack to accomplish what I'm 
discussing.  i.e. User now "has many" UserProfile without any need for an 
intermediate mapping table.  meaning I can get the ideal admin interface.

the rub is that I'd have to insert each UserProfile and then UPDATE the 
self_id to have the same value as "id".

if through_fields allowed "id" I wouldn't have this problem.  but in my 
Django version at least it doesn't allow that.  is there any way around 
this?

Thanks for reading.

-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-users+unsubscr...@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-users/cdd6f6cd-cac2-4516-a1a8-da793012e17b%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.