I have an issue that I am unable to solve. Here is the scenario: there is a retail business which sells cigarettes. This business can have many sites, but each site does not necessarily sell the same types of cigarettes. For this reason, each store owner can browse to a web portal and select the cigarette types that their site sells – these selections will act as columns for a future table. The trouble I am having is writing the data to a database because it is a ManyToMany field. From the data entry perspective, everything appears to be working. The user is able to select what they need and, as a test, I have a HttpResponse message that returns the correct value to the POST method of the view.
The problem that I’m running into is that I don’t know how to save this data in a table because the underlying table is part of a many-to-many relationship. When I try to save the data coming from the form, I get an error:
"Cannot assign "'6565'": "CigCountColumnsMappings.site_id" must be a "Sites" instance."
I have tried many things and I just can’t seem to get this to insert. I think this has something to do with the fact that site_id is a OneToOne/ForeignKey to the Sites model.
My models.py:
class Sites(models.Model):
SITE_STATUS = (
('Open', 'Open'),
('Closed', 'Closed'),
('Maintenance', 'Maintenance')
)
id = models.CharField(max_length=20, verbose_name='Site ID', primary_key=True)
def __str__(self):
return self.id
class CigCountColumns(models.Model):
column = models.CharField(max_length=10, primary_key=True, db_column="column", verbose_name="Column")
def __str__(self):
return str(self.column)
class Meta:
ordering = ["column", ]
verbose_name = "Cigarette Count Column"
verbose_name_plural = "Cigarette Count Columns"
class CigCountColumnsMappings(models.Model):
site_id = models.OneToOneField('home.Sites', on_delete=models.CASCADE, primary_key=True, db_column="site_id", verbose_name="Site ID")
columns = models.ManyToManyField(CigCountColumns, db_column="columns", verbose_name="Cig. Count Column Mappings", blank=True)
def __str__(self):
return str(self.site_id)
class Meta:
ordering = ["site_id", ]
verbose_name = "Cig. Count Column Mapping"
verbose_name_plural = "Cig. Count Column Mappings"
My views.py:
def cigarette_columns(request):
if request.method == "POST":
this_site = request.POST['this_site']
choices = request.POST.getlist('choices')
for choice in choices:
record = CigCountColumnsMappings.objects.create(site_id=this_site, columns=choice)
record.save()
if choices:
return HttpResponse([this_site, choices])
else:
return HttpResponse([this_site, "No data entered"])
The fact that site_id has a reference to the “home.Sites” table is messing me up. I need to insert the values directly into the CigCountColumnsMappings model to map site 6565 to those particular columns but I am unable to do so. I have taken a look at “through” models and read lots of documentation on ManyToMany fields but the solution still eludes me. Many thanks in advance for any help.
First, change site_id to site. This will automatically create a field named site_id that is an integer value for the primary key.
Second, columns is a reference to CigCountColumns, but you assign it as choices which is a list. You need to create a CigCountColumns instance or get one from the database or have the id for a CigCountColumns before you create a CigCountColumnsMappings.
Just as an FYI, I was unable to resolve this issue in a pythonic Django way. I ended up using Django raw SQL queries to hit the table directly (Google: "from django.db import connection") and this resolved it. It's definitely not as "clean" as using the built-in Django methods, but it's not as dirty as completely bypassing Django and using pymysql either.
Related
I am developing a Django website and I have the following models (simplified):
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
opinions = JSONField(default=default_opinions)
class Author(models.Model):
name = models.CharField(max_length=50, unique=True)
class Book(models.Model):
author = models.ForeignKey(Author, on_delete=models.CASCADE, default='0')
with the opinions field being the opinion that a specific user has of the different authors:
Exemple:
{
Shakespeare: 0.5,
Voltaire: 0.6
}
Then I have a listView BookListView, in which I want to query the Book database, and order them by the opinion the currently logged in user has of their author.
In the previous example it would be all the Voltaire's books first, then the Shakespeare's ones.
So I came up with this in my listView:
def get_queryset(self):
user_opinions = self.request.user.profile.opinions
queryset = Book.objects.order_by(user_opinions[F("author__name")])
return queryset
The problem is the F value is computed after the get_queryset(), so I get a F("author__name") key does not exist error.
I thought about iterating through the dict keys and values but I don't see how that could work since opinions are floats (and thus can take any values).
Thanks in advance ^^
F expressions doesn't support JSON field lookup,that's why you are getting error key does not exist, because it is trying to find field named with that and running join on that field,
Can you elaborate more, on what key and value pair are there in json Field
I have multiple databases with different relations and I'm unable to figure out how to properly structure things to avoid problems in Django Admin.
Here is the design I want:
Category
-> 'name' (CharField)
Movie
-> Multiple CharFields
CategoryLink
-> 'category' (ForeignKey -> Category)
-> 'movie' (ForeignKey -> Movie)
-> 'prefix' (ForeignKey -> Prefix) - Blank, Null
-> 'number' (DecimalField)
Prefix
-> 'category' (ForeignKey -> Category)
-> 'prefix' (CharField)
I want to achieve is a structure where:
Each Category can have multiple CategoryLinks to Movies
Movies can exist within any number of Categories any number of times.
CategoryLinks can have a Prefix, but doesn't have to have one.
Prefixes are limited to specific Categories
Editing a Prefix for a specific Category has no effect on a prefix of the same name for a differ Category
Categories can have multiple or zero Prefixes
Prefixes can only be used by a CategoryLink if the Prefix.category == CategoryLink.category
Unrelated Prefixes are hidden from Categories
My main issue is that when I try to limit the choices of Prefixes to only display choices relevant to the current Category I run into a wall. I can do it fine when editing a CategoryLink, but not when adding a new one.
If I try to limit the choices in the Admin Panel with something like this:
#admin.py
# ....
def __init__(self, *args, **kwargs):
super(PrefixForm, self).__init__(*args, **kwargs)
Prefix.objects.filter(category_id=self.instance.category.id)
It works fine while editing, but it throws an error whenever I'm in a form which has the ability to add new entries. Since the new entries doesn't have a defined category.id by virtue (I assume) of not existing yet Django throw an "RelatedObjectDoesNotExist" exception at me.
If I can get it to work while editing an existing Category that would be fine. I don't need to be able to create new CategoryLinks without having a Category already, but when I try I get the same error since I'm using a TabularInline class to include CategoryLinks into Category.
I'm willing to completely restructure everything to get this to work, as you can probably tell I'm new to Django so it's possible I'm thinking about this all wrong.
I asked a similar question last week that I thought I had figured out on my own, but it turned out I simply pushed the problem a step rather than solve it.
Is there a better way to structure my models or is there something I can do within admin.py to properly filter prefixes to categories? Let me know if you want to see all my code, it seemed a bit too much to post here.
I could "solve" this issue by turning CategoryLink.prefix into a CharField, but it would be so much cooler if I could have a separate model for Prefixes.
Here is my full Model.py (minus some irrelevant __unicode__ stuff)
from django.db import models
class Category(models.Model):
class Meta:
verbose_name_plural = "Categories"
name = models.CharField(max_length=255, unique=True)
def category_link_count(self):
return len(CategoryLink.objects.filter(category_id=self.id))
category_link_count.short_description = 'Movies in Category'
class Movie(models.Model):
title = models.CharField(verbose_name='Movie Title', max_length=300)
year = models.DecimalField(verbose_name='Year of Release',
max_digits=4, decimal_places=0)
class CategoryLink(models.Model):
class Meta:
verbose_name = "Category Link"
verbose_name_plural = "Category Links"
category = models.ForeignKey(Category)
movie = models.ForeignKey(Movie)
prefix = models.ForeignKey('Prefix', blank=True, null=True)
number = models.DecimalField(verbose_name='Movie Number', max_digits=2,
blank=True, null=True, decimal_places=0)
class Prefix(models.Model):
category = models.ForeignKey(Category)
prefix = models.CharField(verbose_name='Category Prefix', max_length=255)
class Meta:
verbose_name = "Prefix"
verbose_name_plural = "Prefixes"
ordering = ['-category']
My suggestion is to have defaults for fields that have relations with other models, that way you can create a a Category and have an arbitrary Prefix. However, I think you have structured your models inappropriately.
Category has a ManyToManyField to Prefix which has a FK to Category. You don't need the FK. This is just one, but you can really simplify your organization. I don't know enough about what you want to accomplish.
I have my User model set up with no primary key so that the automatic id will be used instead. However, when I try to access it using Django's "_set" notation when it is referenced through a foreign key:
def postDetails(request, pk)
post = Post.objects.get(pk=pk)
if post.user_set.all(): # Errors on this line
[...]
I get an error from MySQL:
OperationalError at /webApp/postDetail/42/ (1054,
"Unknown column 'user.id' in 'field list'")
What am I doing wrong? Should I be accessing it differently? Are there limitations to the automatic id?
Model for reference:
class Post(models.Model):
name = models.CharField(max_length=128)
blog = models.ForeignKey('Blog')
active = models.BooleanField(blank=True)
created_date = models.DateTimeField()
class Meta:
managed = False
db_table = 'post'
def __unicode__(self):
return self.name
class User(models.Model):
level = models.ForeignKey(Level)
post = models.ForeignKey(Post)
name = models.CharField(max_length=64)
note = models.CharField(max_length=4096)
active = models.BooleanField(blank=True, default=True)
class Meta:
managed = False
db_table = 'user'
Something else that I thought to include: I did run syncdb before running this.
EDIT : wrong answer. check comments below
The problem is that you have managed set to False. According to the documentation
If False, no database table creation or deletion operations will be performed for this model. This is useful if the model represents an existing table or a database view that has been created by some other means. This is the only difference when managed=False. All other aspects of model handling are exactly the same as normal. This includes
Adding an automatic primary key field to the model if you don’t declare it. To avoid confusion for later code readers, it’s recommended to specify all the columns from the database table you are modeling when using unmanaged models.
You will need to define the primary key as this is not done by default anymore.
Not 100% sure, but I think even though Django will add the id field to the model class, that field will not propagate to the DB with syncdb.
One way to try it would be to rename the existing User table, run syncdb and see if the User table is created. If not (which is likely because of the managed flag) try again with managed=True. If the id field appears in this case then my guess is you'll have to add it manually to the User table with the same parameters as the automatically created one.
Let's assume I have the following models:
class Position(models.Model):
name = models.CharField()
class PositionStats(models.Model):
position = models.ForeignKey(Position)
averageYards = models.CharField()
averageCatches = models.CharField()
class PlayerStats(models.Model):
player = models.ForeignKey(Player)
averageYards = models.CharField()
averageCatches = models.CharField()
class Player(models.Model):
name = models.CharField()
position = models.ForeignKey(Position)
I want to perform the equivalent SQL query using django's ORM:
SELECT *
FROM PlayerStats
JOIN Player ON player
JOIN PositionStats ON PositionStats.position = Player.position
How would I do that with django's ORM? The query isn't exactly correct, but the idea is that I want a single query, using django's ORM, that gives me PlayerStats joined with PositionStats based on the player's position.
I've been working with django for a while now and I have had a pretty rough time figuring out the table joins, but I think I finally understand and I would like to pass this on to others so they may avoid the frustration that I had with it.
Consider the following model.py:
class EventsMeetinglocation(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=100)
address = models.CharField(max_length=200)
class Meta:
managed = True
db_table = 'events_meetinglocation'
class EventsBoardmeeting(models.Model):
id = models.IntegerField(primary_key=True)
date = models.DateTimeField()
agenda_id = models.IntegerField(blank=True, null=True)
location_id = models.ForeignKey(EventsMeetinglocation)
minutes_id = models.IntegerField(blank=True, null=True)
class Meta:
managed = True
db_table = 'events_boardmeeting'
Here we can see that location_id in EventsBoardmeeting is a foreign key for the id in EventsMeetinglocation. This means that we should be able to query the information in EventsMeetinglocation by going through EventsBoardmeeting.
Now consider the following views.py:
def meetings(request):
meetingData = EventsBoardmeeting.objects.all()
return render(request, 'board/meetings.html', {'data': meetingData })
As stated many times before in may other posts, django takes care of joins automatically. When we query everything in EventsBoardmeeting we also get any related information by foreign key as well, But the way that we access this in html is a little different. We have to go through the variable used as the foreign key to access the information associated with that join. For example:
{% for x in data %}
{{ x.location_id.name }}
{% endfor %}
The above references ALL of the names in the table that were the result of the join on foreign key. x is essentially the EventsBoardmeeting table, so when we access x.location_id we are accessing the foreign key which gives us access to the information in EventsMeetinglocation.
select_related() and prefetch_related() is your solution. They work almost same way but has some difference.
select_related() works by creating an SQL join and including the fields of the related object in the SELECT statement. For this reason, select_related gets the related objects in the same database query. But it only works for one-to-one or one-to-many relation. Example is below-
entry = Entry.objects.select_related('blog').get(id=5)
or
entries = Entry.objects.filter(foo='bar').select_related('blog')
prefetch_related(), on the other hand, does a separate lookup for each relationship and does the ‘joining’ in Python. This allows it to prefetch many-to-many and many-to-one objects, which cannot be done using select_related. So prefetch_related will execute only one query for each relation. Example is given below-
Pizza.objects.all().prefetch_related('toppings')
It isn't one query, but it's pretty efficient. This does one query for each table involved, and joins them in Python. More on prefetch_related here: https://docs.djangoproject.com/en/dev/ref/models/querysets/#prefetch-related
Player.objects.filter(name="Bob").prefetch_related(
'position__positionstats_set', 'playerstats_set')
In Django 3.2, the framework automatically follows relationships when using method QuerySet.filter()
# The API automatically follows relationships as far as you need.
# Use double underscores to separate relationships.
# This works as many levels deep as you want; there's no limit.
# Find all Choices for any question whose pub_date is in this year
# (reusing the 'current_year' variable we created above).
>>> Choice.objects.filter(question__pub_date__year=current_year)
This compiles to the following SQL query:
SELECT
"polls_choice"."id",
"polls_choice"."question_id",
"polls_choice"."choice_text",
"polls_choice"."votes"
FROM
"polls_choice"
INNER JOIN "polls_question" ON
("polls_choice"."question_id" = "polls_question"."id")
WHERE
"polls_question"."pub_date" BETWEEN 2020-12-31 23:00:00 AND 2021-12-31 22:59:59.999999
See tutorial here: https://docs.djangoproject.com/en/3.2/intro/tutorial02/
From django.db import connection In your view include the below statement:
cursor = connection.cursor()
cursor.execute("select * From Postion ON Position.name = Player.position JOIN
PlayerStats ON Player.name =
PlayerStats.player JOIN PositionStats ON Position.name = PositionStats.player")
solution = cursor.fetchall()
I am currently using Django Users model.
Very simple. However, I'd like to add one feature: Adding friends!
I would like to create 2 columns in my table:
UID (the ID of the User)
friend_id (the ID of his friend! ...of course, this ID is also in Django's User model.
The UID-friend_id combination must be unique! For example, if my ID is 84, I cannot have two rows the same, because I can only subscribe to the same friend once.
Can anyone tell me if this is the right way to do it? Should I do some KEY relationship for the "friend_id", or should I leave it like this, as "IntegerField"?
class Friend(models.Model):
uid = models.ForeignKey(User)
friend_id = models.IntegerField(default=0)
Thank you
You should create a model that defines the relationship between two users, and then define two foreign-key fields, each one to a User. You can then add a unique constraint to make sure you don't have duplicates.
There is a article here explaining exactly how to do this: http://www.packtpub.com/article/building-friend-networks-with-django-1.0
The example model from that page:
class Friendship(models.Model):
from_friend = models.ForeignKey(
User, related_name='friend_set'
)
to_friend = models.ForeignKey(
User, related_name='to_friend_set'
)
def __unicode__(self):
return u'%s, %s' % (
self.from_friend.username,
self.to_friend.username
)
class Meta:
unique_together = (('to_friend', 'from_friend'), )