I have the following models
class Employee(Person):
job = model.Charfield(max_length=200)
class Address(models.Model):
street = models.CharField(max_length=200)
city = models.CharField(max_length=200)
class EmpAddress(Address):
date_occupied = models.DateField()
date_vacated = models.DateField()
employee = models.ForeignKey()
When I build a json data structure for an EmpAddress object using the django serialzer it does not include the inherited fields only the EmpAddress fields. I know the fields are available in the object in my view as I can print them but they are not built into the json structure.
Does anyone know how to overcome this?
Thanks
Andrew
Inheritance of Django models can get a little tricky. Unless you excplicitly require EmpAddress to be subclass of Address, you might just want to duplicate the fields and let duck typing handle the fact that you aren't following traditional object oriented design. E.g:
class Address(models.Model):
street = models.CharField(max_length=200)
city = models.CharField(max_length=200)
class EmpAddress(Address):
street = models.CharField(max_length=200)
city = models.CharField(max_length=200)
date_occupied = models.DateField()
date_vacated = models.DateField()
employee = models.ForeignKey()
Another shot in the dark you might try is to use jsonpickle (I'm one of the developers), which is "smarter" than the standard json module. The latest code has some great new features, thanks to davvid.
Take a look at: http://www.partisanpost.com/2009/10/django-jquery-jqgrid-example-one/1/ as a solution to your problem. The full serializer allows you to drill down into foreignkey relationships as far as you need to go. I wrote a tutorial example of how to use it to integrate django with JqGrid, which provides an example of just what you are faced with. Hope this helps.
John,
This is view code I am using along with the models is;
def address_grid(request):
employeeId = request.GET.get('employeeId')
if request.GET.get('sidx') == '':
order = 'date_occupied'
else:
order = request.GET.get('sidx')
if request.GET.get('sord') == 'asc':
sort_order = ''
else:
sort_order = '-'
order = sort_order + order
if request.GET.get('page'):
paginated = int(request.GET.get('page'))
else:
paginated = 1
items = int(request.GET.get('rows'))
addresses = EmpAddress.objects.filter(employee__id=employeeId)
for add in addresses:
log.write(add.city+'\n') # Field from address object
total = adresses.all().count()
if total % items > 0:
items_sum = 1
else:
items_sum = 0
pages = total / items + items_sum
if paginated > pages:
paginated = 1
addresses = addresses.order_by(order)[paginated-1)*items:paginated*items]
rows = serializers.serialize("json", addresses, indent=4,)
addresses = '{total:%(pages)s, page:%(page)s, records:%(total)s, rows:%(addresses)s' \
% {'pages':pages, 'page':paginated, 'total':total, 'addresses':rows}
log.write(rows+'\n') #json object no Address fields (city is not included)
#even it is present above
return HttpResonse(addresses, mimetype="application/json")
When I print the addresses objects after the
addresses = EmpAddress.objects.filter(employee__id=employeeId)
line I have all of the objects attributes (both the Address and EmpAddress fields).
But when I print the json object I only have the EmpAddress object attributes excluding the Address attributes.
Related
This is a very simple question that got me stuck. I have 2 tables that are connected: Dealershiplistings, Dealerships. On my website I need to display the Model, Make and year of the car (Which are all stored in the DealershipListing, so i have no problem wiht this part) but I also need to print the address that is stored in the Dealerships table. Can anyone help me with this?
this is what i have for my views.py
def store(request):
dealer_list = Dealer.objects.all()
car_list = DealershipListing.objects.all()
context = {'dealer_list': dealer_list, 'car_list': car_list}
return render(request, 'store/store.html', context)
i tried doing
{{%for car in car_list%}}
<h6>{{car.year}} {{car.make}} {{car.model}}</h6>
{% endfor %}
which works perfectly displaying those. But now how do i display the address of the dealership that is selling the car?
models.py
class Dealer(models.Model):
dealersName = models.TextField(('DealersName'))
zipcode = models.CharField(("zipcodex"), max_length = 15)
zipcode_2 = models.CharField(("zipCode"), max_length = 15)
state = models.CharField(("state"), max_length=5)
address = models.TextField(("Address"))
dealerId = models.IntegerField(("ids"), primary_key=True)
def __str__(self):
return self.dealersName
class DealershipListing(models.Model):
carID = models.IntegerField(("CarID"), primary_key=True)
price = models.IntegerField(('price'))
msrp = models.IntegerField(('msrp'))
mileage = models.IntegerField(('mileage'))
is_new = models.BooleanField(('isNew'))
model = models.CharField(("Models"), max_length= 255)
make = models.CharField(("Make"), max_length=255)
year = models.CharField(("Year"),max_length=4)
dealerID = models.ForeignKey(Dealer, models.CASCADE)
def __str__(self):
return self.year + " " + self.make + " " + self.model
So then it looks like your question is really How do I access data from a foreign key in a template?
The answer is refreshingly simple!
{{car.dealerID.address}}
On a side note, you might want to rename dealerID to dealer, django will handle the db column names how it sees fit, so while you might access the data with .dealer the db column would be named dealer_id by django automatically. Renaming the field also makes it more obvious that accessing it will give you a dealer and not its id.
calling with the model name is what I prefer to use
{{obj.related_table.field_name}}
I think this pattern may help you solve problem related to getting related field value
Let's say I have two models:
class Testmodel1():
amount = models.IntegerField(null=True)
contact = models.ForeignKey(Testmodel2)
entry_time = models.DateTimeField()
stage = choicesfiled
class Testmodel2():
name = models.CharField()
mobile_no = models.CharField()
I want to find out the object of Testmodel1 for contact > 3 which is created in the last 24 hours last = arrow.utcnow().shift(hours=-24).date().
I am applying a query:
n1=Testmodel1.objects.filter(entry_time__gte=last, stage=1).annotate(t_count=Count('contact')).filter(t_count__gt=3)
But it seems it's not working. Because I am getting an empty queryset.
Any help would be appreciated.
Only a partial answer. Sorry! Your code looks fine to me, so I'm just trying to find a solution by approaching it from a different direction.
Here's how I structure (sort of) similar code on one of my projects.
from datetime import timedelta, date
....
base_date = date.today()
start_date = base_date + timedelta(days=30)
end_date = base_date
possible_holidays = Holiday.objects.filter(
start_date__lte=start_date, end_date__gte=end_date)
From there, could you just do something like:
if possible_holidays.contact_set.count() > 3:
pass
Does that work?
The problem is your Many-to-One relationship is inverted. This relationship is a parent-child relationship, where a parent can have multiple children, but a children can only have one parent. In database this relationship is stored as a child's ForeignKey field that points to the child's parent.
In your case Testmodel1 is a parent and Testmodel2 is a child (Testmodel1 can have multiple contacts represented by Testmodel2) This means that ForeignKey field should belong to Testmodel2, not Testmodel1.
class Testmodel1():
amount = models.IntegerField(null=True)
entry_time = models.DateTimeField()
stage = choicesfiled
class Testmodel2():
name = models.CharField()
mobile_no = models.ForeignKey()
parent = models.ForeignKey(Testmodel1,
related_name='contacts',
)
With this model structure you can reference Testmodel1's contacts as testmodel1.contacts.all(). Your query then should look like this:
n1 = (Testmodel1.objects
.filter(entry_time__gte=last, stage=1)
.annotate(t_count=Count('contacts'))
.filter(t_count__gt=3)
)
docs reference
I have a History model like below
class History(models.Model):
class Meta:
app_label = 'subscription'
ordering = ['-start_datetime']
subscription = models.ForeignKey(Subscription, related_name='history')
FREE = 'free'
Premium = 'premium'
SUBSCRIPTION_TYPE_CHOICES = ((FREE, 'Free'), (Premium, 'Premium'),)
name = models.CharField(max_length=32, choices=SUBSCRIPTION_TYPE_CHOICES, default=FREE)
start_datetime = models.DateTimeField(db_index=True)
end_datetime = models.DateTimeField(db_index=True, blank=True, null=True)
cancelled_datetime = models.DateTimeField(blank=True, null=True)
Now i have a queryset filtering like below
users = get_user_model().objects.all()
queryset = users.exclude(subscription__history__end_datetime__lt=timezone.now())
The issue is that in the exclude above it is checking end_datetime for all the rows for a particular history object. But i only want to compare it with first row of history object.
Below is how a particular history object looks like. So i want to write a queryset filter which can do datetime comparison on first row only.
You could use a Model Manager method for this. The documentation isn't all that descriptive, but you could do something along the lines of:
class SubscriptionManager(models.Manager):
def my_filter(self):
# You'd want to make this a smaller query most likely
subscriptions = Subscription.objects.all()
results = []
for subscription in subscriptions:
sub_history = subscription.history_set.first()
if sub_history.end_datetime > timezone.now:
results.append(subscription)
return results
class History(models.Model):
subscription = models.ForeignKey(Subscription)
end_datetime = models.DateTimeField(db_index=True, blank=True, null=True)
objects = SubscriptionManager()
Then: queryset = Subscription.objects().my_filter()
Not a copy-pastable answer, but shows the use of Managers. Given the specificity of what you're looking for, I don't think there's a way to get it just via the plain filter() and exclude().
Without knowing what your end goal here is, it's hard to say whether this is feasible, but have you considered adding a property to the subscription model that indicates whatever you're looking for? For example, if you're trying to get everyone who has a subscription that's ending:
class Subscription(models.Model):
#property
def ending(self):
if self.end_datetime > timezone.now:
return True
else:
return False
Then in your code: queryset = users.filter(subscription_ending=True)
I have tried django's all king of expressions(aggregate, query, conditional) but was unable to solve the problem so i went with RawSQL and it solved the problem.
I have used the below SQL to select the first row and then compare the end_datetime
SELECT (end_datetime > %s OR end_datetime IS NULL) AS result
FROM subscription_history
ORDER BY start_datetime DESC
LIMIT 1;
I will select my answer as accepted if not found a solution with queryset filter chaining in next 2 days.
i have three classes
Product have many Descriptions and each model have many stores
what i want to do
select all products but store.qty value > 0
I've tried
pr = Product.objects.all().exclude(Product__Product_description__qty > 0)
how can i do that ?
class Product
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
class Product_description
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
product = models.ForeignKey(Product)
class Store
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
desc = models.ForeignKey(Product_description)
qty = models.IntegerField()
pr = Product.objects.filter(Product_description__qty__lte = 0)
Or if you really must use exclude:
pr = Product.objects.exclude(Product_description__qty__gt = 0)
all() is not necessary in either case; you just end up building an untriggered proxy that goes into building the filter/exclude queryset afterward. It wastes memory and CPU, but otherwise does nothing. Only the .delete() operator requires a working all() queryset, but it's a special case designed explicitly to avoid the accidental destruction of datasets.
The Django Queryset API documentation is very readable.
Django convention is to name your class ProductDescription.
This seems like a backward hierarchy. Why would stores have "product descriptions?" Isn't that metadata on the product itself, and what you care about is that the stores have a certain quantity of product? Or are these product variants, i.e you want to find all the products for which stores have at least one green or blue or orange one? Something tells me that your project needs a careful re-think.
What I want is to be able to get this weeks/this months/this years etc. hotest products. So I have a model named ProductStatistics that will log each hit and each purchase on a day-to-day basis. This is the models I have got to work with:
class Product(models.Model):
name = models.CharField(_("Name"), max_length=200)
slug = models.SlugField()
description = models.TextField(_("Description"))
picture = models.ImageField(upload_to=product_upload_path, blank=True)
category = models.ForeignKey(ProductCategory)
prices = models.ManyToManyField(Store, through='Pricing')
objects = ProductManager()
class Meta:
ordering = ('name', )
def __unicode__(self):
return self.name
class ProductStatistic(models.Model):
# There is only 1 `date` each day. `date` is
# set by datetime.today().date()
date = models.DateTimeField(default=datetime.now)
hits = models.PositiveIntegerField(default=0)
purchases = models.PositiveIntegerField(default=0)
product = models.ForeignKey(Product)
class Meta:
ordering = ('product', 'date', 'purchases', 'hits', )
def __unicode__(self):
return u'%s: %s - %s hits, %s purchases' % (self.product.name, str(self.date).split(' ')[0], self.hits, self.purchases)
How would you go about sorting the Products after say (hits+(purchases*2)) the latest week?
This structure isn't set in stone either, so if you would structure the models in any other way, please tell!
first idea:
in the view you could query for today's ProductStatistic, than loop over the the queryset and add a variable ranking to every object and add that object to a list. Then just sort after ranking and pass the list to ur template.
second idea:
create a filed ranking (hidden for admin) and write the solution of ur formula each time the object is saved to the database by using a pre_save-signal. Now you can do ProductStatistic.objects.filter(date=today()).order_by('ranking')
Both ideas have pros&cons, but I like second idea more
edit as response to the comment
Use Idea 2
Write a view, where you filter like this: ProductStatistic.objects.filter(product= aProductObject, date__gte=startdate, date__lte=enddate)
loop over the queryset and do somthing like aProductObject.ranking+= qs_obj.ranking
pass a sorted list of the queryset to the template
Basically a combination of both ideas
edit to your own answer
Your solution isn't far away from what I suggested — but in sql-space.
But another solution:
Make a Hit-Model:
class Hit(models.Model):
date = models.DateTimeFiles(auto_now=True)
product = models.ForeignKey(Product)
purchased= models.BooleanField(default=False)
session = models.CharField(max_length=40)
in your view for displaying a product you check, if there is a Hit-object with the session, and object. if not, you save it
Hit(product=product,
date=datetime.datetime.now(),
session=request.session.session_key).save()
in your purchase view you get the Hit-object and set purchased=True
Now in your templates/DB-Tools you can do real statistics.
Of course it can generate a lot of DB-Objects over the time, so you should think about a good deletion-strategy (like sum the data after 3 month into another model MonthlyHitArchive)
If you think, that displaying this statistics would generate to much DB-Traffic, you should consider using some caching.
I solved this the way I didn't want to solve it. I added week_rank, month_rank and overall_rank to Product and then I just added the following to my ProductStatistic model.
def calculate_rank(self, days_ago=7, overall=False):
if overall:
return self._default_manager.all().extra(
select = {'rank': 'SUM(hits + (clicks * 2))'}
).values()[0]['rank']
else:
return self._default_manager.filter(
date__gte = datetime.today()-timedelta(days_ago),
date__lte = datetime.today()
).extra(
select = {'rank': 'SUM(hits + (clicks * 2))'}
).values()[0]['rank']
def save(self, *args, **kwargs):
super(ProductStatistic, self).save(*args, **kwargs)
t = Product.objects.get(pk=self.product.id)
t.week_rank = self.calculate_rank()
t.month_rank = self.calculate_rank(30)
t.overall_rank = self.calculate_rank(overall=True)
t.save()
I'll leave it unsolved if there is a better solution.