|
Hi all -
I have two models with a many-to-many relationship: Restaurant and Cuisine. The Cuisine table contains, e.g., "Italian", "Mexican", "Chinese", etc. Each Restaurant record can be associated with one or more Cuisines. Here's the thing: I'd like to limit this to three Cuisines per Restaurant. So when editing the record for "Bob's Pan-Asian Buffet", the user would be able to check "Japanese", "Chinese", and "Korean", but wouldn't be able to check a fourth box. My question: can this be enforced within the model, or is this something I'd have to build into my interface layer? Here's my models.py. from django.db import models from django.contrib.auth.models import User class Restaurant( models.Model ): user = models.ForeignKey( User ) name = models.CharField( max_length = 128 ) slug = models.CharField( max_length = 24, unique = True ) cuisines = models.ManyToManyField( 'Cuisine' ) def __unicode__(self): return self.name class Cuisine( models.Model ): name = models.CharField( max_length = 32 ) def __unicode__(self): return self.name class Meta: ordering = ['name'] -- You received this message because you are subscribed to the Google Groups "Django users" group. To post to this group, send email to [hidden email]. To unsubscribe from this group, send email to [hidden email]. For more options, visit this group at http://groups.google.com/group/django-users?hl=en. |
|
Hi greenie,
you just need to override the save method from your model Restaurant. Before saving each instance, you check if the restaurant already has 3 Cuisine's objects associated. If yes, you don't save the instance and raise an error for exemple, if no, just call the standard method of the super class Model. You will find more infos in the django documentation: http://docs.djangoproject.com/en/dev/topics/db/models/#overriding-predefined-model-methods On 11 mar, 18:06, greenie2600 <[hidden email]> wrote: > Hi all - > > I have two models with a many-to-many relationship: Restaurant and > Cuisine. The Cuisine table contains, e.g., "Italian", "Mexican", > "Chinese", etc. Each Restaurant record can be associated with one or > more Cuisines. > > Here's the thing: I'd like to limit this to three Cuisines per > Restaurant. So when editing the record for "Bob's Pan-Asian Buffet", > the user would be able to check "Japanese", "Chinese", and "Korean", > but wouldn't be able to check a fourth box. > > My question: can this be enforced within the model, or is this > something I'd have to build into my interface layer? > > Here's my models.py. > > from django.db import models > from django.contrib.auth.models import User > > class Restaurant( models.Model ): > > user = models.ForeignKey( User ) > name = models.CharField( max_length = 128 ) > slug = models.CharField( max_length = 24, unique = True ) > cuisines = models.ManyToManyField( 'Cuisine' ) > > def __unicode__(self): > return self.name > > class Cuisine( models.Model ): > > name = models.CharField( max_length = 32 ) > > def __unicode__(self): > return self.name > > class Meta: > ordering = ['name'] -- You received this message because you are subscribed to the Google Groups "Django users" group. To post to this group, send email to [hidden email]. To unsubscribe from this group, send email to [hidden email]. For more options, visit this group at http://groups.google.com/group/django-users?hl=en. |
|
edit: you don't need to raise an error if the restaurant already has 3
Cuisine's objects associated, you just need to return a string with the explanation of the error. On 11 mar, 20:11, gontran <[hidden email]> wrote: > Hi greenie, > > you just need to override the save method from your model Restaurant. > Before saving each instance, you check if the restaurant already has > 3 Cuisine's objects associated. If yes, you don't save the instance > and raise an error for exemple, if no, just call the standard method > of the super class Model. > You will find more infos in the django documentation:http://docs.djangoproject.com/en/dev/topics/db/models/#overriding-pre... > > On 11 mar, 18:06, greenie2600 <[hidden email]> wrote: > > > > > > > > > Hi all - > > > I have two models with a many-to-many relationship: Restaurant and > > Cuisine. The Cuisine table contains, e.g., "Italian", "Mexican", > > "Chinese", etc. Each Restaurant record can be associated with one or > > more Cuisines. > > > Here's the thing: I'd like to limit this to three Cuisines per > > Restaurant. So when editing the record for "Bob's Pan-Asian Buffet", > > the user would be able to check "Japanese", "Chinese", and "Korean", > > but wouldn't be able to check a fourth box. > > > My question: can this be enforced within the model, or is this > > something I'd have to build into my interface layer? > > > Here's my models.py. > > > from django.db import models > > from django.contrib.auth.models import User > > > class Restaurant( models.Model ): > > > user = models.ForeignKey( User ) > > name = models.CharField( max_length = 128 ) > > slug = models.CharField( max_length = 24, unique = True ) > > cuisines = models.ManyToManyField( 'Cuisine' ) > > > def __unicode__(self): > > return self.name > > > class Cuisine( models.Model ): > > > name = models.CharField( max_length = 32 ) > > > def __unicode__(self): > > return self.name > > > class Meta: > > ordering = ['name'] -- You received this message because you are subscribed to the Google Groups "Django users" group. To post to this group, send email to [hidden email]. To unsubscribe from this group, send email to [hidden email]. For more options, visit this group at http://groups.google.com/group/django-users?hl=en. |
|
In reply to this post by greenie2600
Dnia 11-03-2011 o 18:06:43 greenie2600 <[hidden email]> napisał(a):
> Hi all - > > I have two models with a many-to-many relationship: Restaurant and > Cuisine. The Cuisine table contains, e.g., "Italian", "Mexican", > "Chinese", etc. Each Restaurant record can be associated with one or > more Cuisines. > > Here's the thing: I'd like to limit this to three Cuisines per > Restaurant. So when editing the record for "Bob's Pan-Asian Buffet", > the user would be able to check "Japanese", "Chinese", and "Korean", > but wouldn't be able to check a fourth box. > > My question: can this be enforced within the model, or is this > something I'd have to build into my interface layer? > You can limit choices on model level: http://docs.djangoproject.com/en/1.2/ref/models/fields/#django.db.models.ForeignKey.limit_choices_to If query is too complicated (cuz u want to access object's data, witch isn't yet available), you still can limit choices on form level, by overriding __init__ class RestaurantForm(forms.ModelForm): cuisines = forms.ModelMultipleChoiceField(Sklep) class Meta: model = Restaurant def __init__(self, *args, **kwargs): super(RestaurantForm, self).__init__(*args, **kwargs) self.fields[cuisines].queryset = Cuisine.objects.filter(pk__in=[fancy query]) -- Linux user -- You received this message because you are subscribed to the Google Groups "Django users" group. To post to this group, send email to [hidden email]. To unsubscribe from this group, send email to [hidden email]. For more options, visit this group at http://groups.google.com/group/django-users?hl=en. |
|
In reply to this post by gontran
gontran -
Thanks. However, I tried the sample code in your link, and I don't think it will work. Returning a string from the overridden save() method prevents the record from being saved to the database, but by the time the save() method is invoked, my ModelForm (and consequently, I presume, the underlying Model) has already been tested as valid. The Restaurant isn't saved, but the form isn't redisplayed and no error message is shown, and code execution proceeds as if the form were valid (because it *is* valid; it just wasn't saved). I think I need to override the model validation instead. Perhaps I need to override Model.clean_fields()? -- You received this message because you are subscribed to the Google Groups "Django users" group. To post to this group, send email to [hidden email]. To unsubscribe from this group, send email to [hidden email]. For more options, visit this group at http://groups.google.com/group/django-users?hl=en. |
|
In reply to this post by Bugzilla from neostead@go2.pl
bagheera -
I had seen the limit_choices_to parameter, but I thought it controlled *which* choices are available to the user - not *how many* they're allowed to choose. I want to show the user a list of 20 or 30 cuisines, but forbid them from checking more than three. Can you show me an example of how I'd use limit_choices_to to limit the *number* of choices the user can select? On Mar 11, 2:35 pm, bagheera <[hidden email]> wrote: > Dnia 11-03-2011 o 18:06:43 greenie2600 <[hidden email]> napisał(a): > > > Hi all - > > > I have two models with a many-to-many relationship: Restaurant and > > Cuisine. The Cuisine table contains, e.g., "Italian", "Mexican", > > "Chinese", etc. Each Restaurant record can be associated with one or > > more Cuisines. > > > Here's the thing: I'd like to limit this to three Cuisines per > > Restaurant. So when editing the record for "Bob's Pan-Asian Buffet", > > the user would be able to check "Japanese", "Chinese", and "Korean", > > but wouldn't be able to check a fourth box. > > > My question: can this be enforced within the model, or is this > > something I'd have to build into my interface layer? > > You can limit choices on model level: > > http://docs.djangoproject.com/en/1.2/ref/models/fields/#django.db.mod... > > If query is too complicated (cuz u want to access object's data, witch > isn't yet available), you still can limit choices on form level, by > overriding __init__ > > class RestaurantForm(forms.ModelForm): > cuisines = forms.ModelMultipleChoiceField(Sklep) > > class Meta: > model = Restaurant > > def __init__(self, *args, **kwargs): > super(RestaurantForm, self).__init__(*args, **kwargs) > self.fields[cuisines].queryset = > Cuisine.objects.filter(pk__in=[fancy query]) > > -- > Linux user -- You received this message because you are subscribed to the Google Groups "Django users" group. To post to this group, send email to [hidden email]. To unsubscribe from this group, send email to [hidden email]. For more options, visit this group at http://groups.google.com/group/django-users?hl=en. |
|
In reply to this post by greenie2600
Dnia 11-03-2011 o 21:23:38 greenie2600 <[hidden email]> napisał(a):
> gontran - > > Thanks. > > However, I tried the sample code in your link, and I don't think it > will work. Returning a string from the overridden save() method > prevents the record from being saved to the database, but by the time > the save() method is invoked, my ModelForm (and consequently, I > presume, the underlying Model) has already been tested as valid. The > Restaurant isn't saved, but the form isn't redisplayed and no error > message is shown, and code execution proceeds as if the form were > valid (because it *is* valid; it just wasn't saved). > > I think I need to override the model validation instead. Perhaps I > need to override Model.clean_fields()? > Right, i just get to that point, there is a cave rat about limiting choices on form level. Depending on limiting query it may make problems if you edit this object. afik U can't validate m2m fields on model level, but u can do it on form level and rise forms.ValidationError if needed in clean_field(). -- Linux user -- You received this message because you are subscribed to the Google Groups "Django users" group. To post to this group, send email to [hidden email]. To unsubscribe from this group, send email to [hidden email]. For more options, visit this group at http://groups.google.com/group/django-users?hl=en. |
|
In reply to this post by greenie2600
Dnia 11-03-2011 o 21:29:29 greenie2600 <[hidden email]> napisał(a):
> bagheera - > > I had seen the limit_choices_to parameter, but I thought it controlled > *which* choices are available to the user - not *how many* they're > allowed to choose. > > I want to show the user a list of 20 or 30 cuisines, but forbid them > from checking more than three. > > Can you show me an example of how I'd use limit_choices_to to limit > the *number* of choices the user can select? > Form validation. this should work class RestaurantForm(forms.ModelForm): cuisines = forms.ModelMultipleChoiceField(Sklep) class Meta: model = Restaurant def clean_sklepy(self): cuisines_clean = self.cleaned_data[cuisines] if len(cuisines_clean) > 3: raise forms.ValidationError('You can't choose more than three items!') return cuisines_clean -- Linux user -- You received this message because you are subscribed to the Google Groups "Django users" group. To post to this group, send email to [hidden email]. To unsubscribe from this group, send email to [hidden email]. For more options, visit this group at http://groups.google.com/group/django-users?hl=en. |
|
In reply to this post by greenie2600
Dnia 11-03-2011 o 21:29:29 greenie2600 <[hidden email]> napisał(a):
> bagheera - > > I had seen the limit_choices_to parameter, but I thought it controlled > *which* choices are available to the user - not *how many* they're > allowed to choose. > > I want to show the user a list of 20 or 30 cuisines, but forbid them > from checking more than three. > > Can you show me an example of how I'd use limit_choices_to to limit > the *number* of choices the user can select? > > > > On Mar 11, 2:35 pm, bagheera <[hidden email]> wrote: >> Dnia 11-03-2011 o 18:06:43 greenie2600 <[hidden email]> >> napisał(a): >> >> > Hi all - >> >> > I have two models with a many-to-many relationship: Restaurant and >> > Cuisine. The Cuisine table contains, e.g., "Italian", "Mexican", >> > "Chinese", etc. Each Restaurant record can be associated with one or >> > more Cuisines. >> >> > Here's the thing: I'd like to limit this to three Cuisines per >> > Restaurant. So when editing the record for "Bob's Pan-Asian Buffet", >> > the user would be able to check "Japanese", "Chinese", and "Korean", >> > but wouldn't be able to check a fourth box. >> >> > My question: can this be enforced within the model, or is this >> > something I'd have to build into my interface layer? >> or u can add a js script to this form field that will disallow selecting more than three items instead validating model (or do both). -- Linux user -- You received this message because you are subscribed to the Google Groups "Django users" group. To post to this group, send email to [hidden email]. To unsubscribe from this group, send email to [hidden email]. For more options, visit this group at http://groups.google.com/group/django-users?hl=en. |
|
In reply to this post by Bugzilla from neostead@go2.pl
Dnia 11-03-2011 o 21:39:01 bagheera <[hidden email]> napisał(a):
> Dnia 11-03-2011 o 21:29:29 greenie2600 <[hidden email]> > napisał(a): > >> bagheera - >> >> I had seen the limit_choices_to parameter, but I thought it controlled >> *which* choices are available to the user - not *how many* they're >> allowed to choose. >> >> I want to show the user a list of 20 or 30 cuisines, but forbid them >> from checking more than three. >> >> Can you show me an example of how I'd use limit_choices_to to limit >> the *number* of choices the user can select? >> > > Form validation. > > this should work > > class RestaurantForm(forms.ModelForm): > cuisines = forms.ModelMultipleChoiceField(Sklep) > > class Meta: > model = Restaurant > def clean_sklepy(self): > cuisines_clean = self.cleaned_data[cuisines] > if len(cuisines_clean) > 3: > raise forms.ValidationError('You can't choose more than > three items!') > return cuisines_clean > > > > -- Linux user -- You received this message because you are subscribed to the Google Groups "Django users" group. To post to this group, send email to [hidden email]. To unsubscribe from this group, send email to [hidden email]. For more options, visit this group at http://groups.google.com/group/django-users?hl=en. |
|
In reply to this post by Bugzilla from neostead@go2.pl
Can Model.clean() method help you? [1] You'll still have to pay attention to validation before trying to save your instances.[2] Regards, [1] http://docs.djangoproject.com/en/dev/ref/models/instances/#django.db.models.Model.clean [2] http://docs.djangoproject.com/en/dev/releases/1.2/#model-validation -- You received this message because you are subscribed to the Google Groups "Django users" group. To post to this group, send email to [hidden email]. To unsubscribe from this group, send email to [hidden email]. For more options, visit this group at http://groups.google.com/group/django-users?hl=en. |
|
I didn't try it, but werefr0g may be right. It seems that
Model.clean() is the method do you need. And you can still add a javascript control for more convenience without the risk that if a user deactivate javascript, the error will be saved. Let us know greenie if it's ok with this method. On 11 mar, 22:03, werefr0g <[hidden email]> wrote: > Hello, > > Can Model.clean() method help you? [1] You'll still have to pay > attention to validation before trying to save your instances.[2] > > Regards, > > [1]http://docs.djangoproject.com/en/dev/ref/models/instances/#django.db.... > [2]http://docs.djangoproject.com/en/dev/releases/1.2/#model-validation -- You received this message because you are subscribed to the Google Groups "Django users" group. To post to this group, send email to [hidden email]. To unsubscribe from this group, send email to [hidden email]. For more options, visit this group at http://groups.google.com/group/django-users?hl=en. |
|
werefr0g—
Yep, that's actually the solution I'm looking into right now. However, I'm getting an error when trying to save a new instance of the Restaurant model: "'Restaurant' instance needs to have a primary key value before a many- to-many relationship can be used." Here's the new code that's triggering this error: from django.db import models from django.contrib.auth.models import User from django.core.exceptions import ValidationError class Restaurant( models.Model ): user = models.ForeignKey( User ) name = models.CharField( max_length = 128 ) slug = models.CharField( max_length = 24, unique = True ) cuisines = models.ManyToManyField( 'Cuisine', help_text = 'Choose up to three' ) def __unicode__(self): return self.name def clean( self ): raise ValidationError( type( self.cuisines ).__name__ ) class Cuisine( models.Model ): name = models.CharField( max_length = 32 ) def __unicode__(self): return self.name class Meta: ordering = ['name'] As you can see, my custom clean() method simply raises an error containing the type of the cuisines property. And even this is enough to trigger the error shown above. Doing len( self.cuisines ) gives the same result. (Raising an error containing a literal string, without attempting to inspect the cuisines property, works as expected.) Any ideas? It really seems like it should be possible to perform this check at the model level. (bagheera, I'll resort to form-level validation if I have to—but only if I have to. Client-side checks are for UI convenience only, which is a peripheral issue.) I'm new to Django and Python, and at this point I'm more interested in learning the Right Way to do this (if there is one), or *why* I can't do it at the model level (if, in fact, I can't). On Mar 11, 4:11 pm, gontran <[hidden email]> wrote: > I didn't try it, but werefr0g may be right. It seems that > Model.clean() is the method do you need. And you can still add a > javascript control for more convenience without the risk that if a > user deactivate javascript, the error will be saved. > > Let us know greenie if it's ok with this method. > > On 11 mar, 22:03, werefr0g <[hidden email]> wrote: > > > > > > > > > Hello, > > > Can Model.clean() method help you? [1] You'll still have to pay > > attention to validation before trying to save your instances.[2] > > > Regards, > > > [1]http://docs.djangoproject.com/en/dev/ref/models/instances/#django.db.... > > [2]http://docs.djangoproject.com/en/dev/releases/1.2/#model-validation -- You received this message because you are subscribed to the Google Groups "Django users" group. To post to this group, send email to [hidden email]. To unsubscribe from this group, send email to [hidden email]. For more options, visit this group at http://groups.google.com/group/django-users?hl=en. |
|
Dnia 11-03-2011 o 22:45:34 greenie2600 <[hidden email]> napisał(a):
> 'Restaurant' instance needs to have a primary key value before a many- > to-many relationship can be used. That is the answer You need to understand. afiak You can't perform m2m validation on model level due that very reason. That's why i did it on form level, and i think that's the only way to do this. I know such things should be performed on server-side and model level, but i this case, is validation on form level causing any problems? In my project i use only admin interface, so for end-user that validation is completely transparent. -- Linux user -- You received this message because you are subscribed to the Google Groups "Django users" group. To post to this group, send email to [hidden email]. To unsubscribe from this group, send email to [hidden email]. For more options, visit this group at http://groups.google.com/group/django-users?hl=en. |
|
MAX_CUISINES = 3 def clean(self): # three cuisines max. allowed if self.pk is None: # That's the case that raise the error # you're inserting a new Restaurant pass else: # Here, you're editing an existing Restaurant # (including its relashionship with Cuisine) if self.cuisines.count() > MAX_CUISINES: raise ValidationError('I said, "Choose up to three" cusines!') As we're aiming to prevent, at model's level, saving a new Restaurant when too many related Cuisine are provided, I'm afraid that doesn't fit the needs... unless saving restaurant's intance, run a validation then delete it if it fails the validation :) I'd like to place the validation at 'cusines' field level. I'm not confortable placing it at the "whole" model's level, despite my suggestion. I failed for the moment to find a way and I'll resume tomorrow. Maybe the following unsuccessful tries can help you (with a mix of admin interface playing and shell, both on dev server): * use validator on models.ManyToManyField: I can't even trigger a simple print('test') TT * use a custom field by extending models.ManyToManyField, overriding its isValidIDList method Ok, it is a blind test: I was looking for a way to access values from cuisines for a non saved instance of Restaurant and maybe something is lurking there. I'm a beginner. * accessing self's attributes for a non saved or retrieved instance of Restaurant # desesperately random commands (I tried more :): u = Restaurant() r = Restaurant.objects.all()[0] dir(u) # hey, a "cuisines" attribute, great! dir(u.cuisines) # TT, same error you encountered dir(r.cuisines) # fine u.clean_fields() # 'cusines' is not even mentionned in the error message r.clean_fields() # fine [f.verbose_name for f in r._meta.fields] # no cusines I believe that no control is enforced before saving, but if you try to create an new Restaurant and not provide a cuisine, the validation failed from the admin. How does it handle that? I'll try to find out. Is there a way to access data provided to 'cuisines' for an unsaved Restaurant? Regards, -- You received this message because you are subscribed to the Google Groups "Django users" group. To post to this group, send email to [hidden email]. To unsubscribe from this group, send email to [hidden email]. For more options, visit this group at http://groups.google.com/group/django-users?hl=en. |
| Powered by Nabble | Edit this page |
