Django: Can't query a foreign field - python

I'm still a novice so any help is gladly appreciated. Running Django 1.10
I'm trying to retrieve all the profiles that are assigned a particular manager but my query set always comes up empty.
Model.py
Blockquote
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=30, blank=False)
last_name = models.CharField(max_length=30, blank=False)
email = models.EmailField( blank=True, help_text='Optional',)
receive_email_notifications = models.BooleanField(default=False)
manager = models.ForeignKey(User, unique=False, blank=True, related_name='+', null=True)
def get_full_name(self):
"""
Returns the first_name plus the last_name, with a space in between.
"""
full_name = '%s %s' % (self.first_name, self.last_name)
return full_name.strip()
def publish(self):
return self.save
def __str__(self):
return str(self.user)
View.py
Blockquote
def instrument_list(request):
# pulls all instruments from instrument model
instruments = Instrument.objects.all().order_by('instrument_name')
test = Profile.objects.filter(manager='jenn')
print(test)
# pulls all checklists from checklist model
checklists = Checklist.objects.all().order_by('created_date')
# takes instruments, pushes them to template with variable 'instruments'
return render(request, 'blog/instrument_list.html', {'instruments': instruments, 'checklists': checklists})
I've also tried filtering a single profile entry (with a non-foreign key attribute) and printing how managers are saved in the database and the output looked like this
Blockquote
<User: jenn>
However, even when I try filtering with that output, my query sets come up empty
Blockquote
test = Profile.objects.filter(manager='<User: jenn>')
I think I need to adjust my filter parameter to something the database can match against but I'm not sure what that format is. I've tried looking through the documentation but haven't found exactly what I'm looking for.

But that's just a string representation of a model instance. You need the actual instance.
jenn = User.objects.get(username="Jenn")
test = Profile.objects.filter(manager=jenn)
Of course, once you already have jenn as an instance, to can use the reverse accessor of the fk instead:
test = jenn.profile_set.all()
And if you don't have jenn, and you don't need it, you can do the whole thing in one query:
test = Profile.objects.filter(manager__username="Jenn")

I've also tried filtering a single profile entry (with a non-foreign key attribute) and printing how managers are saved in the database and the output looked like this
That's not how managers are saved in databases, thats just a "readable" representation of an instance user.
If you want to filter on a manager you can do something like that :
test = Profile.objects.filter(manager__pk= primary_key_of_manager)
or
temp_manager = User.objects.get(...)
test = Profile.objects.filter(manager=temp_manager)

Related

Django is rendering an awkward result

Basically, I am working on a django project, and whenever I insert data into the database, the result is weirdly formatted.
this is my model
customer.py
class Customer(models.Model):
user = models.OneToOneField(User,null=True,blank=True,on_delete=models.CASCADE)
name = models.CharField(max_length=200, null=True)
email= models.CharField(max_length=200, null=True)
phone_number= models.CharField(max_length=200, null=True)
def __str__(self):
return self.name
Now, say I have saved a new customer
new_customer = Customer.objects.create(name="Henry",email="henry#mail.com",phone_number="+330145786259")
new_customer.save()
when i try to retrieve the customer name i get this:
print(new_customer.name)
>('henry',)
Anyone has any insight for me???
I tried to recreate the model on a new project but still having the same result
In your customer class, you have defined a 1:1 relationship with the in-built user model class of django. And when you are creating the customer object, new_customer, you have not specified the 'user' attribute; hence, your customer object is missing a key element.
The user object already has an in-built field for storing names. It is 'first_name' and 'last_name.' You need to create a user model first before being able to create your 'Customer' model.
Your models.py should look something like this:
from django.contrib.auth.models import User
class Customer(models.Model):
user = models.OneToOneField(User, null=True, blank=True, on_delete=models.CASCADE)
phone_number= models.CharField(max_length=200, null=True)
def __str__(self):
return self.user.first_name
# to return email -> self.user.email
Now to create a 'Customer' object in view.py:
from django.contrib.auth.models import User
from .models import Customer
# create a user object
myuser = User.objects.create_user(username='john', email='jlennon#beatles.com', password='glass onion')
# pass the user object to the customer model
mycustomer = Customer.objects.create(user=myuser, phone_number=123456789)
# save the customer object
mycustomer.save()
Explore django ModelForms to define the user model as per your specifications, e.g, if you don't require your users to have passwords associated with them, etc.
After much testing, I realized why I was getting the weird output.
I was directly passing data from a form to the object creation method, like so:
data = json.loads(request.body)
new_customer = Customer.objects.create(name=data['name'],email="henry#mail.com",phone_number="+330145786259")
new_customer.save()
So assigning the received data to a variable before passing it to the object creation method seems to be the right way of doing things... At least, it is working for me.

DJANGO get objects in sql like join

Context: I'm forcing my self to learn django, I already wrote a small php based website, so I'm basically porting over the pages and functions to learn how django works.
I have 2 models
from django.db import models
class Site(models.Model):
name = models.CharField(max_length=50, unique=True)
def __str__(self):
return self.name
class Combo(models.Model):
username = models.CharField(max_length=50)
password = models.CharField(max_length=50)
dead = models.BooleanField(default=False)
timestamp = models.DateTimeField(auto_now_add=True)
siteID = models.ForeignKey(Site, on_delete=models.PROTECT)
class Meta:
unique_together = ('username','siteID')
def __str__(self):
return f"{self.username}:{self.password}#{self.siteID.name}"
When creating a view, I want to get the Combo objects, but I want to sort them first by site name, then username.
I tried to create the view, but get errors about what fields I can order by Cannot resolve keyword 'Site' into field. Choices are: dead, id, password, siteID, siteID_id, timestamp, username
def current(request):
current = Combo.objects.filter(dead=False).order_by('Site__name','username')
return render(request, 'passwords/current.html',{'current':current})
Since I'm not necissarily entering the sites into the database in alphabetical order, ordering by siteID wouldn't be useful. Looking for some help to figure out how to return back the list of Combo objects ordered by the Site name object then the username.
You can order this by siteID__name:
def current(request):
current = Combo.objects.filter(dead=False).order_by('siteID__name','username')
return render(request, 'passwords/current.html',{'current':current})
since that is the name of the ForeignKey. But that being said, normally ForeignKeys are not given names that end with an ID, since Django already adds an _id suffix at the end for the database field.
Normally one uses:
class Combo(models.Model):
# …
site = models.ForeignKey(Site, on_delete=models.PROTECT)
if you want to give the database column a different name, you can specify that with the db_column=… parameter [Django-doc]:
class Combo(models.Model):
# …
site = models.ForeignKey(
Site,
on_delete=models.PROTECT,
db_column='siteID'
)

Connect and present data from two different tables in django

I'm trying to easily present data from two different tables (classes). I have an Environment class with all the environments details and a Changes class which contain history changes on all my environments.
My view is currently showing all my Environment details. I want to add to this view the last change been made on each environment (e.g last modified by: User).
My models.py look like this:
class System(models.Model):
system_name = models.CharField(max_length=40, blank=True)
system_id = models.CharField(max_length=100, blank=True)
system_clusters = models.ManyToManyField(Cluster, blank=True)
system_owner = models.CharField(max_length=20, blank=True)
def __str__(self):
return self.system_name
class Changes(models.Model):
date = models.DateTimeField(auto_now_add=True)
cluster = models.ForeignKey(System, on_delete=models.CASCADE)
user = models.CharField(max_length=20, blank=True)
change_reason = models.CharField(max_length=50, blank=True)
def __str__(self):
return self.date
At first, i though to pass a dictionary to my template with the system as a key and a change as a value:
last_changes = {}
change = Changes.objects.filter(cluster__in=s.system_clusters.all()).order_by('-id')[0]
last_changes[s.system_id] = change.change_reason
Even though it partially works (I still trying to parse the dict in my template), I feel like this is not the right approach for the task.
I'm hoping to reach a result where I can just call system.last_change in my template. Can I add another field for System class that will point to his last_change in the Changes table?
You can write a method on System to return the last change for an item:
def last_change(self):
return self.changes_set.order_by('-date').first()
Now you can indeed call system.last_change in the template.

NOT NULL constraint failed when linking a model with User

I have a model:
class UserProfile(models.Model):
user = models.OneToOneField(User)
first_name = models.CharField(max_length=30, default='anon')
last_name = models.CharField(max_length=30, default='anon')
interest = models.CharField(max_length=30, default='nothing')
def __str__(self):
return 'Username:%s' % self.user.username
class Post(models.Model):
title = models.TextField(default='No title')
text = models.TextField(max_length=220)
vote = models.IntegerField(default=0)
user_post = models.ForeignKey(User, related_name='post')
class Comments(models.Model):
name = models.TextField(default='Anon', null=True)
comment = models.TextField(max_length=2000, null=True)
post = models.ForeignKey(Post)
def __str__(self):
return 'comment:%s' % self.comment
In the post you can see I'm linking a post with the User. I read that you can access a users comments this way by using user.post_set.all(), so I gave it a try. When I attempt to migrate the change(adding the foriegn key in Post), I get an error.
django.db.utils.IntegrityError: NOT NULL constraint failed:
boardapp_post__new.user_post_id
I notice the post__new, so here is my view named new, which creates a post.
def new(request):
if request.method == 'POST':
post = Post.objects.create(title=request.POST.get('post_title', ''),
text=request.POST.get('post_text',''),vote=0
)
return HttpResponseRedirect(reverse('view_post', args=(post.id,)))
return render(request, 'new.html')
I'm new to creating users in django and am confused as to where not null consraint is failing. Thanks
When you migrate to your new database format, there should be a default User for the user_posts that already exist.
Django will try to fill this value for you in existing user_posts, but it doesn't know which value to chose, since null is not allowed in your current model.
Thus: you need to tell Django either 1) not to worry about Posts without a user (null = True) or 2) supply a default User, which is a bit harder (probably would require a function call that creates some dummy User on the fly).
So the easiest solution is to alter your Post model and change user_post:
user_post = models.ForeignKey(User, related_name='post', null=True)
It is failing because your model field is
user_post = models.ForeignKey(User, related_name='post')
it means every post will be assigned to a user(not null field).
From your view, you are not assigning any user to the post, so change your code to
post = Post.objects.create(title=request.POST.get('post_title', ''),
text=request.POST.get('post_text',''), vote=0, user_post=request.user
)

Getting data from the database with python(on Django framework)

ordinarily if I were writing a sql statement to this I would do something like this,
SELECT * FROM (django_baseaccount LEFT JOIN django_account ON django_baseaccount.user_id = django_account.baseaccount_ptr_id)
LEFT JOIN django_address ON django_account.baseaccount_ptr_id = django_address.user_id;name
how do I put this into the Djagno way of querying the database using the API, i.e.
TradeDownloads.objects.filter(online=1)[:6]
My models
BASE ACCOUNT
class BaseAccount(models.Model):
user = models.ForeignKey(User, unique=True)
def __unicode__(self):
"""
Return the unicode representation of this customer, which is the user's
full name, if set, otherwise, the user's username
"""
fn = self.user.get_full_name()
if fn:
return fn
return self.user.username
def user_name(self):
"""
Returns the full name of the related user object
"""
return self.user.get_full_name()
def email(self):
"""
Return the email address of the related user object
"""
return self.user.email
ACCOUNT
class Account(BaseAccount):
"""
The account is an extension of the Django user and serves as the profile
object in user.get_profile() for shop purchases and sessions
"""
telephone = models.CharField(max_length=32)
default_address = models.ForeignKey(Address, related_name='billing_account', blank=True, null=True)
security_question = models.ForeignKey(SecurityQuestion)
security_answer = models.CharField(max_length=200)
how_heard = models.CharField("How did you hear about us?", max_length=100)
feedback = models.TextField(blank=True)
opt_in = models.BooleanField("Subscribe to mailing list", help_text="Please tick here if you would like to receive updates from %s" % Site.objects.get_current().name)
temporary = models.BooleanField()
def has_placed_orders(self):
"""
Returns True if the user has placed at least one order, False otherwise
"""
return self.order_set.count() > 0
def get_last_order(self):
"""
Returns the latest order that this customer has placed. If no orders
have been placed, then None is returned
"""
try:
return self.order_set.all().order_by('-date')[0]
except IndexError:
return None
def get_currency(self):
"""
Get the currency for this customer. If global currencies are enabled
(settings.ENABLE_GLOBAL_CURRENCIES) then this function will return
the currency related to their default address, otherwise, it returns
the site default
"""
if settings.ENABLE_GLOBAL_CURRENCIES:
return self.default_address.country.currency
return Currency.get_default_currency()
currency = property(get_currency)
def get_gateway_currency(self):
"""
Get the currency that an order will be put through protx with. If protx
currencies are enabled (settings.ENABLE_PROTX_CURRENCIES), then the
currency will be the same returned by get_currency, otherwise, the
site default is used
"""
if settings.ENABLE_PROTX_CURRENCIES and settings.ENABLE_GLOBAL_CURRENCIES:
return self.currency
return Currency.get_default_currency()
gateway_currency = property(get_gateway_currency)
ADDRESS
class Address(models.Model):
"""
This class encapsulates the data required for postage and payment mechanisms
across the site. Each address is associated with a single store account
"""
trade_user = models.BooleanField("Are you a stockist of N Products", help_text="Please here if you are a Stockist")
company_name = models.CharField(max_length=32, blank=True)
line1 = models.CharField(max_length=200)
line2 = models.CharField(max_length=200, blank=True)
line3 = models.CharField(max_length=200, blank=True)
city = models.CharField(max_length=32)
county = models.CharField(max_length=32)
postcode = models.CharField(max_length=12)
country = models.ForeignKey(Country)
account = models.ForeignKey('Account')
class Meta:
"""
Django meta options
verbose_name_plural = "Addresses"
"""
verbose_name_plural = "Addresses"
def __unicode__(self):
"""
The unicode representation of this address, the postcode plus the county
"""
return ', '.join((self.postcode, str(self.county)))
def line_list(self):
"""
Return a list of all of this objects address lines that are not blank,
in the natural order that you'd expect to see them. This is useful for
outputting to a template with the aid of python String.join()
"""
return [val for val in (self.line1, self.line2, self.line3, self.city, self.county, self.postcode, self.country.name) if val]
"ordinarily if I were writing a sql statement"
Welcome to ORM. You're not writing SQL so remove this from the question. Do not ever post SQL and ask how to translate SQL into ORM. Translating SQL limits your ability to learn. Stop doing it.
Write down what the result is supposed to be.
It appears that you are getting all Account objects. Period.
At some point in a view function or template you want to get an Address, also.
for a in Account.objects.all():
a.default_address # this is the address that SQL brought in via a "join".
That's it. Please actually do all the examples in the Django tutorial. Actually type the code from the examples and see how it works.
All "join" operations are SQL workarounds. They're a weird SQL-ism, and have nothing to do with the underlying objects. So stop using SQL terminology to describe what you want.
Django provides a clean way to fall-back to native SQL for complex queries see the official documentation: Performing raw SQL queries
Forget the SQL. What do you want to achieve from this query? What do you want to do with the results?
You haven't posted your models. Do they have the foreign keys defined? Can you just do a simple query and use select_related() to get the joined objects?
Edited to add What was wrong with the answer given the previous time you asked this question?
Edited again but everyone has shown you how to get the item via the foreign key! Forget the id, you don't need it. If you have an Account object a, you just do a.default_address to get the actual Address object that is related. If that doesn't work, then you're not posting the right models, as that will definitely work with the models you have posted.

Categories