views:

21

answers:

2

I have a multi model with different OneToOne relationships from various models to a single parent. Consider this example:

class Place(models.Model):
    name = models.CharField(max_length=100)
    address = models.CharField(max_length=100)
    ...

class Restaurant(models.Model):
    place = models.OneToOneField(Place)
    ...

class Shop(models.Model):
    place = models.OneToOneField(Place)
    ...

Regardless if the above model makes sense in real life scenario, how can someone identify if an object of Place has a relationship to any of the other models, in django view?

+1  A: 

In a template, you can say

{% if place.restaurant %}
    <!-- stuff goes here -->
{% endif %}

The reason for this is that OneToOneField entries actually create an attribute in the model they are referencing. In normal Python code, saying place.restaurant where no restaurant is defined will throw an exception, but templates will swallow such exceptions.

If you do need to do something like this in Python code, the simplest-to-understand way is to wrap it in a try/except block:

place = Place.objects.all()[0]
try:
    restaurant = place.restaurant
    # do something with restaurant
except ObjectDoesNotExist:
    # do something without restaurant

EDIT: As I say in my comment, if you only want a Place to be either a Restaurant or a Shop but never both, then you shouldn't be using OneToOneField and should instead use model inheritance.

Assuming that a Place could have two or more other possibilities, I recommend doing something like this:

class Place(Model):
    # define your fields here

    # you could generate this automatically with trickery
    OTHER_MODELS = ["restaurant", "shop"]

    @property
    def relationships(self):
        if not hasattr(self, "_relationships"):
            self._relationships = {}
            for attr in OTHER_MODELS:
                try:
                    self._relationshops[attr] = getattr(self, attr)
                except ObjectDoesNotExist:
                    pass
        return self._relationships

The above would let you say place.relationships and get back a dictionary that looked like

{"restaurant": <Restaurant Object>, "shop": <Shop Object>}

although one or both of those might be missing, depending on whether they exist. This would be easier to work with than having to catch a potential exception every time you reverse the OneToOneField relationship.

Eli Courtwright
That's the way to go if there wasn't multiple possibilities for a single Place object i.e. Shop and Restaurant. otherwise you'd have to iterate through every single "sub-model". or am I missing something?
msebai
@msebai: Yes, you will have to iterate through the other possibilities. If your design calls for `Place` to only ever be a `Restaurant` or a `Shop` but not both, then you shouldn't be using `OneToOneField` and should instead use model inheritance: http://docs.djangoproject.com/en/1.2/topics/db/models/#model-inheritance
Eli Courtwright
I guess inheritance makes a lot of sense here. I just need to find out how django will treat this on DB level. Many thanks for your time.
msebai
A: 

Because Restaurant has a foreign key pointing into Place, it leaves a related name field on the class, so that the pointed-to class (Place) can find its contents:

# python
import yourproject.settings
from django.db.models.base import ObjectDoesNotExist
try:
  r = place.restaurant_set.get()
  do_something( r.restaurant_field )
except ObjectDoesNotExist:
  print "place has no restaurant"

And from a template, assuming you have access to placeobject from your context somehow:

{% with placeobject.restaurant_set.get as r %}
  {% if r %}
    {{ r.restaurant_field }}
  {% else %}
    No restaurant
  {% endif %}
{% endwith %}
eruciform