I have two models:
class Actor(models.Model):
name = models.CharField(max_length=30, unique = True)
event = models.ManyToManyField(Event, blank=True, null=True)
class Event(models.Model):
name = models.CharField(max_length=30, unique = True)
long_description = models.TextField(blank=True, null=True)
I want to create a form that allows me to identify the link between the two models when I add a new entry. This works:
class ActorForm(forms.ModelForm):
class Meta:
model = Actor
The form includes both name and event, allowing me to create a new Actor and simultaneous link it to an existing Event.
On the flipside,
class EventForm(forms.ModelForm):
class Meta:
model = Event
This form does not include an actor association. So I am only able to create a new Event. I can't simultaneously link it to an existing Actor.
I tried to create an inline formset:
EventFormSet = forms.models.inlineformset_factory(Event,
Actor,
can_delete = False,
extra = 2,
form = ActorForm)
but I get an error
<'class ctg.dtb.models.Actor'> has no ForeignKey to <'class ctg.dtb.models.Event'>
This isn't too surprising. The inlineformset worked for another set of models I had, but this is a different example. I think I'm going about it entirely wrong.
Overall question: How can I create a form that allows me to create a new Event and link it to an existing Actor?
Personally, I would put the ManyToMany on Event to begin with, but to each their own...
As for how to do it, you'd want to write a custom ModelForm (not an inline formset), let's call it EventForm. It would handle all of your event's fields and would also have a ModelChoiceField or ModelMultipleChoiceField to allow selection of the Actor(s) involved. Then in your view you would split out the processing of the Event fields and the ForeignKey/M2M field.
Make sense? alt text http://sonicloft.net/im/52
Related
I'm new to Django (and it's been a few years since I played with databases), and might be dancing around something simple that I don't know what to google.
Essentially, I want to have a table of "Events" which can contain data of various Types. Say one event is an "alarm" and another is a "message." The "alarm" type needs different fields than the "message" so it seems to make sense to not store the data in one big table containing any sort of data that might be needed for any event. However, I want to be able to get a list of all events for a given "event_block" foreign key.
I have done some reading about abstract models and proxies, etc, but I haven't been able to figure out how one Model can refer to an abstract model (if that is in fact the path to take).
models.py:
class Event(models.Model):
#id auto-generated primary_key
event_block = models.ForeignKey(Block, on_delete=models.CASCADE)
event_data = BaseEvent # This is wrong, but what is right?
class BaseEvent(Models.model):
class Meta:
abstract=True
... common event info ...
class AlarmEvent(BaseEvent):
... event info for alarms ...
class MessageEvent(BaseEvent):
... event info for messages ...
Ideally I would be able to, in my template, create a drop down box with all available event_types and then click a "submit" button the form to create that event (with its necessary event data fields) within my Event model and let Django handle the details of how that is handled in the database tables.
What am I missing?
Firstly, This is appropriate with the Event model
class Event(models.Model):
event_block = models.ForeignKey(Block, on_delete=models.CASCADE)
event_data = models.ForeignKey(BaseEvent)
However... BaseEvent with abstract=True does not physically exist but its children AlarmEvent and MessageEvent does. So I am not sure you can point BaseEvent as a ForeignKey. This post about generic foreign key may help.
Secondly about the structure of models. IMO, things can be simpler without abstract mother class. How about a model with category field?
CATEGORY = ( ('field1', 'field name 1'), ('field2', 'field name 2'), )
class Event(models.Model):
category = models.CharField(max_length=10, choices=CATEGORY)
# SOME OTHER FIELDS...
you can see a form of the model via django admin in default.
What is the reason for those separate tables? Are you going to query them directly? Are these events supposed to share data?
If answers to these questions are "I'm not sure" (or even better "no") then how about (instead of creating separate tables) you keep all the data on Event model directly? Something like
class Event(models.Model):
#id auto-generated primary_key
event_block = models.ForeignKey(Block, on_delete=models.CASCADE)
event_data = models.TextField()
and keep the data in event_data field in serialized format (e.g. JSON). Or you can use some plugin, e.g. https://github.com/bradjasper/django-jsonfield for JSONField.
Some common fields can be separated, for example:
class Event(models.Model):
#id auto-generated primary_key
event_block = models.ForeignKey(Block, on_delete=models.CASCADE)
event_type = models.CharField(max_length=2)
event_data = models.TextField()
Here's a version of my models.py file. I've removed irrelevant fields and the model names are made up, for security reasons:
class FilmStudio(models.Model):
name = models.CharField(max_length=200, unique=True)
class ProductionCompany(models.Model):
name = models.CharField(max_length=200)
film_studio = models.ForeignKey(FilmStudio)
class Meta:
# Ensure that a given combination of ProductionCompany name and FilmStudio object is unique
unique_together = ('name', 'film_studio')
class Film(models.Model):
title = models.CharField(max_length=200)
production_company = models.ForeignKey(ProductionCompany)
class Actor(models.Model):
name = models.CharField(max_length=200)
films = models.ManyToManyField(Film, blank=True)
Although it is not explicitly defined, there is a many-to-many relationship between an Actor and a FilmStudio. This is evidenced by the following call to the Python API:
FilmStudio.objects.filter(productioncompany__film__actor__name='Samuel L. Jackson').distinct()
This returns all of the FilmStudio objects which Samuel L. Jackson is related to, and each one only once. What I'd like is to define extra fields on the relationship between an Actor and a FilmStudio (it doesn't work too well in this example, I know, but it makes sense for my scenario).
Following what is described in Extra fields on many-to-many relationships, I could use an intermediate model to define extra fields on the relationship between a Film and an Actor, for instance.
But this doesn't seem to help me with my problem. I don't want to define the Actor to FilmStudio relationship explicitly, since it's an existing relationship based on other relationships.
Is it possible to define fields on the relationship that I'm describing?
As far as I know, you are not able to do that.
The reason for that is that it is nowhere to store the extra fields of that relationship. If I understand you correctly, these "extra fields" are not implicit in the actor-film or productionstudio-film relationships, so even though you say they are implicit, the extra fields themselves are explicit.
You could try to emulate it by creating an explicit direct relationship whenever it is needed. Then you could simulate the extra fields using the model as an abstraction, but I am not sure if this is what you want. If you opt for this kind of solution you can use default values (in your abstraction) for filling in relationships that don't have a instance yet.
Does that explanation make sense to you?
EDIT:
(I have not double checked that the code works, so be vary)
OK, so you have the original models:
class FilmStudio(models.Model):
name = models.CharField(max_length=200, unique=True)
class ProductionCompany(models.Model):
name = models.CharField(max_length=200)
film_studio = models.ForeignKey(FilmStudio)
class Meta:
# Ensure that a given combination of ProductionCompany name and FilmStudio object is unique
unique_together = ('name', 'film_studio')
class Film(models.Model):
title = models.CharField(max_length=200)
production_company = models.ForeignKey(ProductionCompany)
class Actor(models.Model):
name = models.CharField(max_length=200)
films = models.ManyToManyField(Film, blank=True)
# The "solution" would be:
class ActorProductionComapny(models.Model):
production_company = models.ForeignKey(ProductionCompany, related_name='actors')
actor = models.ForeignKey(Actor, related_name='companies')
# your extra fields here
someproperty = models.CharField(max_length=200)
class Meta:
# let's say one per actor
unique_together = ('production_company', 'actor')
This is going to get messy really quickly
We use a F object like this:
FilmStudio.objects.filter(productioncompany__film__actor__name='Samuel L. Jackson',
productioncompany__film__actor=F('actors__actor'),
actors__someproperty="Plays poker with CEO").distinct()
The tricky part is going to be handling default values (i.e. when there is no value) This would have to be implemented using a custom Manager, but then I am out of my depth.
I will try to explain as well as I can, but it's going to be tricky.
If you want to make a filter on the relationship you may have to do something like this:
def filter_prod(pq_query, someproperty, actor_name):
if someproperty == "Default":
# Great, this means we can ignore the parallel relationship:
return pq_query.filter(productioncompany__film__actor__name=actor_name)
else:
# Here comes the hard part
FilmStudio.objects.filter(productioncompany__film__actor__name=actor_name,
productioncompany__film__actor=F('actors__actor'),
actors__someproperty=someproperty).distinct()
The thing I am trying to illustrate here is that there are two kinds of actor-productioncompany relationships, those with custom field values (non-default), and those without.
Now, you can make a custom getter that looks something like this:
class ProductionCompany(models.Model):
name = models.CharField(max_length=200)
film_studio = models.ForeignKey(FilmStudio)
def get_actors(self):
# This one is not lazy, so be aware
actors = list(self.actors)
# Get a list of actor IDs
actor_ids = [a.actor_id for a in actors]
for actor in Actor.objects.filter(films__production_company_id=self.id):
if actor.id not in actor_ids:
actors.append(ActorProductionComapny(actor=actor, production_company=self)
actor_ids.append(actor.id)
return actors
class Meta:
# Ensure that a given combination of ProductionCompany name and FilmStudio object is unique
unique_together = ('name', 'film_studio')
This should not save the relationship to the database until you call .save() on an instance. You can also add a custom save method that ignores/aports .save() calls where all the values are default. Just remember to check if it is a new instance or not, because you don't want it to cancel a "set back to default" call. You could also make it delete on a "set back to default", but check if you are allowed to do that within .save().
For even more complex queries (mix of default and non-default) you have Q-objects (further down on the page from F objects)
In short, you need to create an extra model to store this extra relational data between Actor and FilmStudio.
class Actor(models.Model):
name = models.CharField(max_length=200)
films = models.ManyToManyField(Film, blank=True)
film_studios = models.ManyToMany(FilmStudio, through='ActorFilmStudio')
class ActorFilmStudio(models.Model):
actor = models.ForeignKey(Actor)
film_studio = models.ForeignKey(FilmStudio)
# define extra data fields here
data1 = models.TextField()
data2 = models.IntegerField()
One way to think about this: the data you're trying to store belongs to an Actor-FilmStudio relation, and is not related in anyway to Film or ProductionCompany.
Your existing ability to retrieve the a set of Actors for a given FilmStudio (or vice-versa) does not necessarily imply you can store relational data belonging to these two models using the models defined in your example.
Keep in mind that each of the models you defined in your example are backed by a table in your database. In the case of Actor.films field, Django creates an extra table to store the many-to-many relationship data.
Since you're looking to store relational data between Actor and FilmStudio, you need to consider where the data will be stored in your database. Can you store the data in the Film model? or the ProductionCompany model?
I have made an api that returns an object as json data. I Am using the django-rest-framework and its serializer. Using the resources (ModelResource) I excluded some fields, like a property called 'owner'. One of the fields is a foreignkey to itselve. I want to show this field in the api (so I use depth=2), but I want to exclude the same fields as I excluded in the object returning.
Is there a nice way to do this (I have tried several things without the wanted result).
This is my (simplified) code:
in models.py:
class MyObject(models.Model):
name = models.CharField(max_length=256, blank=True)
parent = models.ForeignKey('self', blank=True, null=True, default=None)
and_some_otherfields = models.otherFields(....)
owner = models.ForeignKey(User, null=True, blank=True, related_name='myobject_owner')
in resource.py:
class MyObjectResource(ModelResource):
model = MyObject
exclude = ('owner','and some other fields',)
and in the view used to return the object it returns this:
data = Serializer(depth=2).serialize(my_object)
return Response(status.HTTP_200_OK, data)
In the response it leaves out the exclude fields (as I wanted and expected).
but in the field parent, the parent myobject with all fields I want to hide.
I Am looking for a way to indicate that for this parent object, the serializer should use the same Resource, or add the secundary fields to the exclude list....
If I use depth =1 it only shows whether it has a parent ([]), or null if not, and i do need to know at least the parent's ID.
Ah, i just found it:
I need to add in the resource for all fields I want to show what resource....
fields = ('name', ("parent","MyObjectResource") , 'and all the other fields you want to see as well...')
I found it here: google groups forum question
You can skip the exlude, it ignores it, and just add the fields you want to show, you do not have to define them, unless you need to indicate what resource to use.
So following is the final code of the resource.py part:
class MyObjectResource(ModelResource):
model = MyObject
fields = ('name', ("parent","MyObjectResource"), 'and all the other fields you want to see as well...')
Here is how an other solution could be.
class ProToPicturesSerial(serializers.ModelSerializer):
pro_pictures = PictureSerializer(many=True)
pro_videos = VideoSerializer(many=True)
city_pro = CitySerializer(many=True)
class Meta:
model = Province
fields = ('id', 'name', 'intro', 'description', 'display_url', 'pro_pictures', 'pro_videos', 'city_pro')
I am working with an existing database that I can not modify and having some trouble trying to deal with presenting forms for modifying the database in Django. The structure in question is as follows and all models are unmanaged.
class Persons(models.Model):
personid = models.BigIntegerField(primary_key=True, db_column='PersonID')
....
class Phones(models.Model):
phoneid = models.BigIntegerField(primary_key=True, db_column='PhoneID')
number = models.CharField(max_length=60, db_column='Number', blank=True)
type = models.CharField(max_length=15, db_column='Type', blank=True)
...
class Personsphones(models.Model):
personphoneid = models.BigIntegerField(primary_key=True, db_column='PersonPhoneID')
personid = models.ForeignKey(Persons, db_column='PersonID')
phoneid = models.ForeignKey(Phones, db_column='PhoneID')
...
I want to create a form to display all of the 'Phones' associated with a particular 'Persons' and in addition be able to modify/add/remove 'Phones' belonging to a 'Persons'. Right now the only thing I can think of is to display the 'Phones' in a modelformset and then if one is added or removed manually set the 'Personsphones' relation. Any ideas on how to best deal with this model setup?
For making changes to your models you may want to use django-south http://south.aeracode.org/docs/
As far as displaying your 'Phone' under your forms.py you may want to set up class meta like so. With this any changes made to models will reflect on change
class Meta:
model = Persons
exclude = ('user')
In models you may want to use Foreignkey fore relationships between phones and Persons. Better seen in action here https://docs.djangoproject.com/en/dev/ref/models/fields/#foreignkey
I have four models in my models.py which are:
models.py
class Course(models.Model):
course_code = models.CharField(max_length=100,unique=True)
title = models.CharField(max_length=200)
short = models.CharField(max_length=50)
elective_group = models.CharField(max_length=100)
class Unit(models.Model):
title = models.CharField(max_length=100)
short = models.CharField(max_length=50)
course = models.ForeignKey(Course)
class Pattern(models.Model):
pattern_name = models.CharField(max_length=200)
class ExamSchedule(models.Model):
exam_date = models.DateTimeField()
course = models.ForeignKey(Course)
pattern = models.ForeignKey(Pattern)
units = models.ManyToManyField(Units)
I have all these models register with admin site, so that i can use admin functionality for these models.
My problem is when a user creates or edits a ExamSchedule object , i want the units(field) multivalue widget should contains only those values that are associated with a course as every course can have multiple units. So if user creates an Examschedule object and after selecting a course from dropdown the unit widget should only contains those units that related to the course selected.
Django-Smart-Select could have been useful but it only supports foreign key chained and grouped selects nor ManyToManyField chained select.
Thanks
You can send the selected course with a Ajax request and do this to get the related units.
Lets say you select communication networks (cn) as the course you can get the related units like so:
cn_units = Unit.object.filter(course = 'cn').values_list('id',flat=True)
This will return a single list of all the related units to that course.
You can send this list as a response to your ajax request, iterate over this list and populate the select box for unit in ExamSchedule form. I consider plain ajax because its very flexible.