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
Related
How can I concatenate two different model query and order by a field that both models has like progress fields.
For example
models.py
class Gig(models.Model):
author= models.ForeignKey(User)
title = models.CharFields()
progress = models.IntegerField()
class Project(models.Model):
author= models.ForeignKey(User)
title = models.CharFields()
progress = models.IntegerField()
Can I do my view.py like this, for me to achieve it?
İf No, How can I achieve it?
views.py
def fetch_all_item(request):
gig = Gig.objects.filter(author_id = request.user.id)
project = Project.objects.filter(author_id = request.user.id)
total_item = (gig + project).order_by("progress")
return render(request, "all_product.html", {"item": total_item})
I am trying to join two query set from Gig and Project models then send it to frontend in an ordering form by a field name called progress.
You can let Python do the sorting for you, like:
from operator import attrgetter
def fetch_all_item(request):
gig = Gig.objects.filter(author=request.user)
project = Project.objects.filter(author=request.user)
total_item = sorted([*gig, *project], attrgetter('progress'))
return render(request, "all_product.html", {'item': total_item})
It might however be better to remodel this to a single model with a type field that disambiguates between a Gig and a Project.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
In general, such design when there are common fields is better accomplished by inheriting from some base class. E.g.:
class CommonActivity(models.Model):
# common fields for Project/Gig
author = models.ForeignKey(User)
title = models.CharFields()
progress = models.IntegerField()
class Gig(CommonActivity):
pass # or other fields related to Gig only
class Project(CommonActivity):
pass
Then if you want to query both - you query CommonActivity.
If remodel not possible - then filter via Python as #WillemVanOnsem suggested.
However such filtering will be way less efficient
class Cities(models.Model):
city_main_image = models.FileField()
city_name = models.CharField(max_length=200)
city_info = models.CharField(max_length=1000)
city_images = models.FileField()
In my models.py I have Cities class and I want to upload several images for this class variable, to be clearly for city_images variable how can I do this? Are there any way to do this or not?
Few notes about your code before answering your question.
1) Stick to singular model names, City rather than Cities.
2) Don't repeat the model name in every field, so you get "main_image",
"name", "info", "images".
3) Use ImageField for images rather than FileField.
4) No need to have 2 fields for main_image and images. You can add an extra field to make the image a main image or not.
Now, to answer your question, you need to read about relations in an SQL database.
To use relations between your models with django's ORM, look at https://docs.djangoproject.com/en/2.0/ref/models/fields/#django.db.models.ForeignKey
You have 2 options: ForeignKey or ManyToManyField. Stick with the ForeignKey as you don't need a many to many relation.
So you'll have something like the following:
class City(models.Model):
...
class CityImage(models.Model):
image = models.ImageField(...)
city = models.ForeignKey(City) # you might need to add on_delete parameter depending on your django version.
or
class CityImage(models.Model):
image = models.ImageField(...)
class City(models.Model):
...
images = models.ManyToManyField(CityImage)
class Cities(models.Model):
city_main_image = models.FileField()
city_name = models.CharField(max_length=200)
city_info = models.CharField(max_length=1000)
class CityImages(models.Model):
city_id = models.ForeignKey(Cities)
city_images = models.FileField()
Now each of your city in Cities can have one or more images in another model called CityImages. If we talk in terms of tables then the primary key for a row in table Cities would be associated to one or more rows in table city_images. I will strongly suggest you to go through official introductory tutorial of django. Also I personally find this tutorial very helpful for beginners. Just in case it helps.
One way you could do this is to make a CityImage model, which would allow you to make a ForeignKey to the City model, with a related_name=images for the reverse lookup.
class City(models.Model):
name = models.CharField(max_length=200)
info = models.CharField(max_length=1000)
#property
def main_image(self):
try:
return self.images.get(primary=True)
except CityImage.DoesNotExist:
pass
except CityImage.MultipleObjectsReturned:
# Handle this case, the application should ensure only one is set to `True` at a time...
class CityImage(models.Model):
city = models.ForeignKey(City, related_name='images')
primary = models.BooleanField(default=False)
image = models.FileField()
I've got this Post model at the moment:
class Post(models.Model):
title = models.CharField(max_length = 140)
body = models.TextField()
date = models.DateTimeField()
def __unicode__(self):
return self.title
If I've got different parts of a website (or a forum rather) that contain different posts, e.g. Discussion about basketball, and Discussion about football, if I wanted to return just posts concerning basketball or just posts concerning football, is the easiest way to just make a specific basketball_post model/football_post model or is there a more efficient way? Should I perhaps be storing the values differently?
Thanks
Django has a really good tutorial. It is about making a Poll app. In the first chapter the thing you want is discussed. It is about a Question that can have multiple Choices.:
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
class Choice(models.Model):
question = models.ForeignKey(Question)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
The foreignKey creates a relation between two models. The same can be done for a blog:
class Category(models.Model):
title = models.CharField(max_length=200)
class Post(models.Model):
category = models.ForeignKey(Category) # This is the important part.
title = models.CharField(max_length=200)
body = models.TextField()
date = models.DateTimeField()
def __unicode__(self):
return self.title
The ForeignKey relation lets you do really nice things:
basketball_posts = Post.objects.filter(category_title='Basketball')
But before we all tell you how it is done, I really recommend to do the tutorial. It introduces you to all important Django concepts: https://docs.djangoproject.com/en/1.7/intro/tutorial01/
Update
If you have a fixed set of categories that are not likely to change, than you can hardcode them and use field choices:
class Post(models.Model):
FOOTBALL = 'F' # Variable name and db_value
CRICKET = 'C'
INTRODUCTION = 'I'
CATEGORY_CHOICES = (
(FOOTBALL, 'Soccer'), # Variable name and display value
(CRICKET, 'Cricket'),
(INTRODUCTION, 'Hello my name is'),
)
category = models.CharField(max_length=1,
choices=CATEGORY_CHOICES,
default=INTRODUCTION)
...
https://docs.djangoproject.com/en/dev/ref/models/fields/#choices
One of the advantages of this 'choice machinery' over a CharField without pre defined choices is that you are sure what values end up in your database. This lets you query them, without worrying if your data is sane:
Post.objects.filter(category=Post.CRICKET)
Use the extra table if you need the freedom to create new categories in the future. Use field choices if you don't want (or need) that freedom.
I would suggest to just add a field which makes the post relevant to that certain topic:
class Post(models.Model):
title = models.CharField(max_length = 140)
body = models.TextField()
date = models.DateTimeField()
type = models.CharField(max_length=20) #<--- new field: e.g 'basketball','hockey'..
def __unicode__(self):
return self.title
example query:
#basketball posts
qs = Post.objects.filter(type__icontains="basketball")
then you dont need to have multiple models which also would be redundant.
Assuming all of the posts are in the same format, you could add another field to your model like "type". Different discussion forums could send a different values for that field when the post is added.
type = models.CharField(max_length=140, choices=['Football', 'Basketball', 'Baseball'])
Storing this would make it easy to filter which posts are which.
Post.objects.filter(type = 'Football')
Assuming that one post can be about only one sport, the better approach would be to have a foreign key relation between a model that stores data about a post with another model that stores the data about sports.
Something like this
class Sport(models.Model):
name = models.CharField(max_length = 200)
description = models.TextField()
def __unicode__(self):
return self.name
class Post(models.Model):
title = models.CharField(max_length = 140)
body = models.TextField()
date = models.DateTimeField()
sport = models.ForeignKey(Sport)
def __unicode__(self):
return self.title
This gives you the advantage of isolating the 'Sport' and the 'Post' models.You can add as many sports as you want, without any posts referring to it.
One more advantage is that you can add relevant information to the relevant models.
Eg:Suppose you want to add the information about "how many players are there in a team for sport x?". You can easily achieve this by adding a field "number_of_players" in the 'Sport' model without affecting the 'Post' model.
If you had to do this in one model, 'Post', then it would create lot of issues in terms of data consistency and other undesirable things.
Also, the query will look something like this:
posts = Post.objects.filter(sport__name = "Basketball")
PS:If your requirement is that a post can be tagged to multiple sports, then you can use ManyToMany field instead of a simple foreign key.
https://docs.djangoproject.com/en/dev/topics/db/examples/many_to_many/
You could assign your posts tags or category, and filter on those.
If you use the model approach what happens when you add more sports? You'll need manually add the sports in your code, using a tags or category approach allows you to handle it in the db, and would then allow you to filter on the tags/categories in your system
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.
I am really new to django, and I have some questions about how django's M2M works and also how it works with the modelForm or the forms.Form am currently working on an application that contains the following models:
class Agent(models.Model):
user = models.ForeignKey(User)
assigned_by = models.ForeignKey('self')
date = models.DateField(auto_now_add=True)
def __str__(self):
return str(self.user)
class Region(models.Model):
name = models.CharField(max_length=50)
# Agent to Region many-to-many
class ARM2M(models.Model):
agent = models.ForeignKey(Agent)
region = models.ForeignKey(Region)
is_manager = models.BooleanField(default=False)
Constraints:
an agent can be in multiple regions, and he/she can be one of the managers in any of those regions that he/she is in.
Question#1: is there a way to present such logic with the many-to-many feature in django model?
-end-of-question#1-
So I have this AgentForm for an agent manager to add an new agent in his/her regions. But I know this form is not working: because in the MultipleChoiceField, I have no way to get the current loggedin agent, to present the available regions. In other words, I am stuck.
class AgentForm(forms.Form):
agent = forms.IntegerField(widget=forms.HiddenInput())
regions = forms.MultipleChoiceField(choices=RAM2M.objects.filter(agent=agent, is_manager=True))
def save(self):
agent_id = self.cleaned_data['assigned_by']
agent = Agent.objects.get(pk=int(agent_id))
self.instance.assigned_by = agent
super(AgentForm, self).save()
class Meta:
model=Agent
exclude = ('assigned_by')
Question#2: How do I construct a form for an existing agent to add a new agent to the regions where he/she is a manager.
Thanks,
Maxim
Question #1: you can specify an intermediate model for the M2M relationship using the through argument:
class Agent(models.Model):
...
regions = models.ManyToManyField(Region, through='ARM2M')
(See http://docs.djangoproject.com/en/dev/topics/db/models/#intermediary-manytomany)
Regarding question #2, if think that this has the answer you're looking for: Creating a dynamic choice field