django admin many-to-many with intermediate model add new - python

I have the following models (simplified):
class Concert(models.Model):
title = models.CharField(max_length=50)
date = models.DateField()
songs = models.ManyToManyField(Song,
through='RunOrder',
related_name='run_order',
blank=True)
class Song(models.Model):
title = models.CharField(max_length=50)
class RunOrder(models.Model):
concert = models.ForeignKey(Concert, on_delete=models.CASCADE)
song = models.ForeignKey(Song, on_delete=models.CASCADE)
act_no = models.SmallIntegerField()
scene_no = models.SmallIntegerField()
Basically, songs can be in multiple concerts, concerts have multiple songs.
While creating a new concert in the admin view, I would like to be able to add new songs. Here's what I have:
class ConcertAdmin(admin.ModelAdmin):
...
inlines = [SongInline]
class SongInline(admin.TabularInline):
model = RunOrder
show_change_link = True
extra = 1
But this only lets me select from existing songs. In order to add a new song, I have to use the Song admin interface. When I tried to use Song as the model for SongInline, I got a has no ForeignKey error. Is there a way I can streamline/inline the process of adding new Songs to a Concert?

It turns out this is a default feature, but the admin interface has to be enabled for the Song model:
admin.site.register(Song)
Then you get the little plus sign. Screenshot of django admin inline table.

Related

Django Model Field Inline

In Django, you can use inline to make new instances of models by just clicking the add instance button on the bottom of the admin site.
Is there a way to do this for a model field inside a model.
For example, if you were cataloging a book and you want to capture all the chapters in each books name. Every book has a different number of chapters so you could not just do
chapter1 = CharField(max_length = 200)
chapter2 = CharField(max_length = 200)
and so on. Like the picture below but not for model instances for model fields.
Yes, you can model the Book and its chapters with:
class Book(models.Model):
title = models.CharField(max_length=256)
class Chapter(models.Model):
title = models.CharField(max_length=256)
book = models.ForeignKey(Book, on_delete=models.CASCADE)
then we can work with a TabularInline [Django-doc], and write this as:
from django.contrib import admin
class ChapterInline(admin.TabularInline):
model = Chapter
class BookAdmin(admin.ModelAdmin):
inlines = [
ChapterInline,
]

How can I create a (select image) like field inside django form?

I am creating an order-app which enables the user to order custom name tags. I need to create a field that enables the user to select his/her desired background. Every background will have a key or id. I am currently representing this field using a PositiveIntegerField because there is going to be a 100 plus backgrounds so it is time-consuming to import them as choices inside a list.
from django.db import models
class Sticker(models.Model):
Lan = [
('ar', 'عربي'),
('en', 'English'),
]
name_field = models.CharField(max_length=40)
school_field = models.CharField(max_length=40)
grade_field = models.CharField(max_length=40)
quantity = models.PositiveSmallIntegerField()
language = models.CharField(choices=Lan, max_length=2)
sticker_number = models.PositiveIntegerField()
#image = models.ImageField(max_length=1, null= True)
status = models.BooleanField(default=False)
and this is how the form looks like, just used a simple ModelForm class to pass the fields.
from .models import Sticker, CustomerDetails, DeliveryDetails
class Meta:
model = Sticker
fields = ('name_field', 'school_field', 'grade_field', 'quantity', 'language', 'sticker_number')
So I need a way to show the user images and to be able to select an image. I am trying to build this project without using previously build templates or forms and I am still a beginner in python and django.

Django - Keep track of comments

I am building a web app, where each product has its own "Profile". I need to add to the model some kind of field where i can add "Comments", with date and text, for keeping track of info such as change in formula, change of provider, change in price, etc.
Any ideas?
models.py
from django.db import models
# Create your models here.
class Horse(models.Model):
name = models.CharField(max_length=255)
nacimiento = models.DateField(blank=True, null=True)
nro = models.IntegerField()
event = models.TextField()
slug = models.SlugField(unique=True)
def __str__(self):
return '%s-%s' % (self.name, self.nro)
So for every event that happens, i need a new entrance with the description provided in the text field.
class HorseTracker(models.Model):
horse = models.ForeignKey(Horse, on_delete=models.CASCADE, related_name='horse')
comment = models.CharField(max_length=128)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-created_at']
Each time you change something in your model you can create new instance of HorseTracker with description of changes you've made.
To make it more usefull you can use TabularInline in your HorseAdmin
class HorseTrackerInline(admin.TabularInline):
model = HorseTracker
class HorseAdmin(admin.ModelAdmin):
list_display = ['name', 'nacimiento', 'nro', 'event', 'slug', ]
inlines = [ HorseTrackerInline, ]
If you want to track various models I would suggest to use something like django-simple-history to keep track of the changes in your model.
Adding a history field to the model lets you save all the changes made to the fields and then access the history. If you want to add a custom message you can add fields to the historical model, and in a signal set the message.
from simple_history.models import HistoricalRecords
class MessageHistoricalModel(models.Model):
"""
Abstract model for history models tracking custom message.
"""
message = models.TextField(blank=True, null=True)
class Meta:
abstract = True
class Horse(models.Model):
name = models.CharField(max_length=255)
birthdate = models.DateField(blank=True, null=True)
nro = models.IntegerField()
event = models.TextField()
slug = models.SlugField(unique=True)
history = HistoricalRecords(bases=[MessageHistoricalModel,])
Then using signals you can get changes using diff and then save a custom message stating the changes an who made them.
from django.dispatch import receiver
from simple_history.signals import (post_create_historical_record)
#receiver(post_create_historical_record)
def post_create_historical_record_callback(sender, **kwargs):
history_instance = kwargs['history_instance'] # the historical record created
# <use diff to get the changed fields and create the message>
history_instance.message = "your custom message"
history_instance.save()
You could generate a pretty generic signal that works for all your models tracked with a 'history' field.
Note: I renamed "nacimiento" as "birthdate" to keep consistency in naming all the fields in english.

Implementing a model for "teams" in django

I want to implement a team feature in django 1.8. (Team as in sports team)
Every user can join up to one team at a time and a team thus can hold many users. Now i am unsure how to define my models.py
I started with this core, but now i am unsure how to make the connection of Team<->User
from django.db import models
class Team(models.Model):
name = models.CharField(max_length=64, unique=True)
description = models.TextField(max_length=1024)
logo = models.ImageField()
from django.contrib.auth.models import User
class Player(models.Model):
user = models.OneToOneField(User)
team = ForeignKey('Team')
Do I now create a second class user_team or do I just add the team as a foreign key to the user? (and if thats the way where would i need to do this?)
Thanks,
Wegi
// edit: I added some code at the bottom. Would this Player model be enough to define the relationship?
For this use case, I will still suggest an alternative using a ManyToMany field, with an intermediate model and model manager.
A quick sample structure looks like this:
from django.db import models
from django.contrib.auth.models import User
class Team(models.Model):
name = models.CharField(max_length=64, unique=True)
description = models.TextField(max_length=1024)
logo = models.ImageField()
players = models.ManyToManyField(User, through='Player')
class PlayerManager(models.Manager):
use_for_related_fields = True
def add_player(self, user, team):
# ... your code here ...
def remove_player(self, user, team):
# ... your code here ...
def trasnfer_player(self, user, team):
# ... your code here ...
class Player(models.Model):
user = models.ForeignKey(User)
team = models.ForeignKey(Team)
other_fields = #...
objects = PlayerManager()
Usage:
Player.objects.add_player(user, team, *other_fields)
You will then be able to get User related Team, for example:
team_with_user = Team.objects.filter(players__name="hello")
user_in_team = User.objects.filter(team__name="world")
Note: I haven't tested the code, so please correct me if I make any mistake above.
The reason why I prefer this way is to abstract away your database logic into application. So in future if there is a need for allowing User joining multiple teams, you can just change the application logic to allow it through the manager.
As suggested by #aumo I solved the problem by adding a user profile model like this:
from django.contrib.auth.models import User
class Player(models.Model):
user = models.OneToOneField(User)
team = models.ForeignKey('Team')
I chose this solution over adding teams as a ManyToMany field inside the Teams class because I am not sure if any more field need to be added to the Player during development.
Thanks to everybody for your help.

Listing foreign keys related to a proxy model in django admin

My models.py looks like this :
class Author(models.Model):
name = models.CharField()
class DraftBooks(models.Model):
title = models.CharField()
author = models.ForeignKey(Author)
status_choices = ((1,Draft),(2,Published))
status = models.SmallIntegerField(choices=status_choices)
class PubBooks(DraftBooks):
class meta:
proxy = True
verbose name = 'Published Books'
I am using a proxy model since I want a different change list view for books in draft state and books which have been published.To achieve this,my admin.py looks like this :
class AuthorAdmin(admin.ModelAdmin):
pass
admin.site.register(Author, AuthorAdmin)
class DraftBooksAdmin(admin.ModelAdmin):
list_display = ('title','author','status')
def queryset(self):
return DraftBooks.objects.filter(status='1')
admin.site.register(DraftBooks, DraftBooksAdmin)
class PubBooksAdmin(admin.ModelAdmin):
list_display = ('title','author','status')
def queryset(self):
return PubBooks.objects.filter(status='2')
admin.site.register(PubBooks, PubBooksAdmin)
This setup works perfectly fine.In my admin,now I have 3 change list views,one which shows a list of all authors,one which shows book which are in a draft state and finally one which shows the list of books which are in a published state.
I now need to add a hyperlink to every item (Author) in the authors' list overview that links to a view showing all books of the specific authors.For Example:
J.K. Rowling (books)
J.R.R. Tolkien (books)
where books is a hyperlink to a site showing all books of a particular author.
Now I am completely clueless as to how to do this.Django Xadmin has a plugin which provides just this feature.This Stackoverflow question also provides answer to this problem.But the problem is that they do not work in proxy models with the custom filters that I have.When I try to get the list of books by an author,I get only the books which are in Draft state.I would ideally want all the books,Draft and Published by an author.How do i achieve this ?

Categories

Resources