Django Adding a ManyToManyField to ModelForm - python

Sorry that this is like the thousandth question for this issue but I still can't see a light at the end of the tunnel.
Lets say I have two models:
class Video(models.Model):
title = models.CharField(u"Titel",max_length=200)
slug = AutoSlugField(populate_from='title',unique=True)
date = models.DateField("Datum")
description = models.TextField(u"Beschreibung")
user = models.OneToOneField(User, blank=True, null=True)
class Channel(models.Model):
name = models.CharField(u"Name",max_length=30)
slug = AutoSlugField(populate_from='name',unique=True)
videos = models.ManyToManyField('videoportal.Video',related_name="contained_videos",blank=True,null=True)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
As you see I want to have a channel with video(s) in it. So if I ad a video using a ModelForm like this
class VideoForm(ModelForm):
class Meta:
model = Video
the form I get will not contain a input field to select a channel (of course not). So how can I do this? How can I have a input field in my form to select one channel with a drop down?
Thanks,
Philip

If a video only belongs in one channel just give your Video model a ForeignKey to your Channel model. If it should belong to more than one channel I'd use a ManyToManyField in the Video model, as already suggested.
I think this would fit the idea of uploading videos and adding it to a channel far better than doing it the other way around.

Try putting the ManyToMany field in the Video model and omit it from the Channel model:
class Video(model.Model):
...
channels = model.ManyToManyField('videoportal.Channel', related_name='videos')
...
If you want a simple dropdown to select a single channel, why is it a many-to-many realationship between videos and channels?

Use a custom form instead of Django ModelForm.
probably something like this,
class VideoForm(forms.Form):
title = forms.CharField()
description = forms.TextField()
channel = forms.ModelChoiceField(queryset= Channel.objects.all(), empty_label=None)
do your validation in a view. Use Model save() method to save information contained in your POSTed form.

Related

How do I limit a django model's field choices using one of the previous fields?

The following is in my models.py:
class SensorType(models.Model):
hardware_type = models.CharField(max_length=100)
is_static = models.BooleanField(default=False)
# Some other fields
class Sensor(models.Model):
device_id = models.CharField(max_length=100, primary_key=True)
sensor_type = models.ForeignKey(SensorType, on_delete=models.PROTECT)
# Some other fields
class Asset(models.Model):
name = models.CharField(max_length=100)
sensor_type = models.ForeignKey(SensorType, on_delete=models.PROTECT) # I need to use this field to filter below
sensor = models.ForeignKey(Sensor, on_delete=models.PROTECT, limit_choices_to={'sensor_type': WHAT DO I PUT HERE?},)
# Some other fields
I need to limit the choices in the sensor field of asset so that only sensors with the sensor_type set in the field immediately above, show up.
The reasoning behind this is that there will eventually be many sensors and it would be very useful to filter this. Initially I only need this to work from the admin page but this will eventually extend when I make my Create and Update Views.
Is this even possible? I'm essentially trying to access attributes before the object has actually been created.
After reading several other questions such as this one I have also looked into ModelChoiceField but the same issue exists of trying to access the form data before it has been submitted.
I'm very open to changing the model structure if that is what is required.

How to create Create for ManyToMany relation with through?

I have models like:
class Playlist(models.Model):
key = models.CharField(max_length=255, blank=True, unique=True)
user = models.ForeignKey(User)
title = models.CharField(max_length=200)
pub_date = models.DateTimeField(auto_now_add=True)
videos = models.ManyToManyField(Video, through='PlaylistVideo')
class PlaylistVideo(models.Model):
playlist = models.ForeignKey(Playlist)
video = models.ForeignKey(Video)
position = models.IntegerField()
class Video(models.Model):
title = models.CharField(max_length=255,blank=True)
description = models.TextField(blank=True)
thumb = models.URLField(blank=True)
duration = models.IntegerField(default=0)
I want to write POST (create) for Playlist an API. Not sure how to handle videos field.
How to send values for videos field?
Django 1.8 and django-rest-framework
Thanks
From DRF documentation :
By default, relational fields that target a ManyToManyField with a through model specified are set to read-only.
So you most definitely CAN NOT create Video instances when creating a Playlist.
Now, considering you already have Video objects, and you want to create a Playlist which contains some of those videos, there are two ways to go about this.
You create your m2m relations at the same time as you create the Playlist :
class PlaylistSerializer(ModelSerializer):
videos = PrimaryKeyRelatedField(many=True)
class Meta:
model = Playlist
You only create the Playlist on POST, without any videos, and then you create another endpoint (e.g. playlist/<playlist_id>/videos/) where you add/remove the videos that you want.
Both have pros and cons, but my opinion is that you should first design you API, thinking also about how you will continue editing that Playlist.
Do you add videos one by one ? Do you add them in bulk ?
How do you remove a video ? Can that be done in bulk ?
Once you've got the right answer to those questions, implementation should be fairly simple.
Good luck!

django ModelForm foreign key - text widget add if not exist

I am just starting out with Django and have the following:
models.py:
class Song(models.Model):
name = models.CharField(max_length=200, blank=False, null=False)
artist = models.ForeignKey(Artist, blank=False, null=False)
class Artist(models.Model):
name = models.CharField(max_length=200, unique=True)
Now I have a model form for Song but currently have no ability to add artists that don't already exist (rendered as a dropdown). It would be nice to allow users to add artists on the fly but haven't been able to find a way to get that working. I saw lots of answers relating to replicating the admin "add another..." but kept running into roadblocks and outdated information.
What I tried:
replicating the "add another" from the admin console
Creating as a regular form - but abandoned it because ModelForms gives me a lot for free
Started researching on formsets but got stuck there, could not find a working example
Is there a way to easily add another artist from the song form? I don't mind rendering a new textbox below the artist selection where the user can add a new artist but I don't know how to do that with ModelForms and then add the artist into the database before saving.
Any advice would be much appreciated!
It would help to see what you are using to create your forms. I assume you are using a ModelFrom. If you are using jQuery I think you could use the following in your forms.py to capture a new artist. However, if you are using jQuery I would save individual forms as templates and display them as necessary depending on a button or link event for a new artist.
forms.py
class SongForm (forms.ModelForm):
new_artist_name = forms.CharField()
class Meta:
model = Song
def save(self, commit=True):
# do something with self.cleaned_data['new_artist']
new_artist = Artists.objects.filter('new_artist_name')
if new_artist.exists():
# Save song to artist.
else:
# Create and save new artist and save song to the
# new artist.
return super(SongForm, self).save(commit=commit)

Building a Python Django model with multiple images

I have the following models in Django:
class SpaCenter(models.Model):
name = models.CharField(max_length=50)
street = models.CharField(max_length=200)
house_name = models.CharField(max_length=100)
house_number = models.IntegerField()
city = models.CharField(max_length=50)
country = models.CharField(max_length=50)
region = models.CharField(max_length=50)
class SpaCenterImages(models.Model):
spacenter = models.ForeignKey(SpaCenter)
image = models.ImageField()
primary_image = models.BooleanField()
class SpaCenterWebsites(models.Model):
spacenter = models.ForeignKey(SpaCenter)
website = models.CharField(max_length=300)
PART 1:
I would like:
1) the "SpaCenter" model to have multiple images belonging to it, and 2) One of those images to be selectable as the "primary" image.
Whats the best way to go about building a django model for that? I think I got (1) right...but overall would like to hear some advice on model design.
Basically the same principle as Facebook - 1 profile can have multiple images.
PART 2:
How would one go about scripting the Django admin console for these models in order to allow for multiple images to be uploaded against one profile.
I am quite new to Django, so help is very appreciated. Thank you.
I implemented similar functionality as yours before. To me it's not so clear how to restrict one primary_image for SpaCenter in the model level, but your model design seems to be enough to achieve what you want.
What you can do is to use django formset to make sure there's only one primary exists. Here's a rough idea(not tested):
class SpaCenterImageFormSet(BaseModelFormSet):
def clean(self):
super(SpaCenterImageFormSet, self).clean()
primary_count = 0
for form in self.forms:
if 'primary_image' in form.cleaned_data and \
form.cleaned_data['primary_image']:
primary_count += 1
if self.forms and primary_count == 0:
raise ValidationError("You must have one primary image!")
if primary_count > 1:
raise ValidationError("You cannot have more than one primary image!")
One last thing, don't use plural form in model names, django will add it for you when needed, or you can customize it yourself.
For your part 2 of the question, use normal admin for SpaCenter and Inline for SpaCenterImage https://docs.djangoproject.com/en/1.8/ref/contrib/admin/#inlinemodeladmin-objects

django Getting unwatched items

class Photo(models.Model):
...
viewsT = models.ManyToManyField('PhotoViewT', symmetrical=False)
...
class PhotoViewT(models.Model):
user = models.ForeignKey(User)
creationdate = models.DateTimeField()
I store photos' information in "Photo" table and information about views in "PhotoViewT" table (Here is "user" who watched photo and "creationdate" when he watched it). On "photo" I have m2m field to views, where I add information about all views.
My task is to get photos, those haven't been already watched by current defined user. I have no clue how to craft this query.
Something like
Photo.objects.filter(viewsT__user__doesnt_contain=targetUser)
Expression above hardly does work. Any solutions? Thank you in advance!
Ref the doc. Try
Photo.objects.exclude(viewsT__user=targetUser)
Photo.photoviewtset.all() will fetch you all PhotoViewT instances associated with that Photo.
Photo.photoviewtset.filter(user=<your_current_user) will fetch you all PhotoViewT instances associated with that Photo filtered by your current_user.
Note: You do not need the ManyToManyField in the Photo model for this
Photo.objects.exclude(viewsT__in=PhotoViewT.objects.filter(user=request.user))
OR
Photo.objects.filter(~Q(viewsT__in=PhotoViewT.objects.filter(user=request.user)))

Categories