ContentType in a model not being saved correctly - django

I have a model that is essentially a tag:
class Tag( models.Model ):
tag = models.CharField( max_length=15 )
text_color = RGBColorField(default='#000000')
background_color = RGBColorField( default='#88ff88' )
entered_by = models.ForeignKey( 'member.Member', related_name='tag_entered_by' )
entered_on = models.DateTimeField( default=timezone.now )
content_type = models.ForeignKey(ContentType, related_name="tag_content_type")
def __str__(self):
return self.tag
A few days ago I created a few tags associated with a particular model based on the ContentType. Everything was working fine and "I didn't change anything".
I then created some Tags from the shell:
>> class_object=get_model('call', 'Call')
>> ct = ContentType.objects.get_for_model(class_object)
>>> tag = Tag(tag='thetag', text_color='#FFFFFF', background_color='#B40303', content_type=ct, entered_by=automem)
>>> tag.content_type
<ContentType: Call>
>>> tag.save()
>>> tag.content_type
<ContentType: Call>
>>>
>>>
>>> ts = Tag.objects.filter(tag='thetag')
>>> for t in ts:
... print(t.content_type)
...
invoice
The "invoice" is a different model. So, I don't understand why, in this case, the content_type is not being saved properly.
I am using Django 1.8 and Python 3.4.
[Added]
A little more information:
>>> ct
<ContentType: Call>
>>> ct.id
24
But when I do this:
>>> for t in ContentType.objects.all():
... print('{} {}'.format(t, t.id))
I get
...
invoice 24
...
Call 28
...

Related

Django 'exclude' query did not return expected results

I am relatively new to Django and this is my first post in the forum.
Below are the models(simplified) that are used in the app.
The app is about reserving a set of resources for a given period.
from django.db import models
class Resource(models.Model):
id = models.AutoField(primary_key=True)
serialno = models.CharField(max_length=30, null=False, unique=True)
name = models.CharField(max_length=40, null=False)
def __str__(self):
return f"{self.name}/{self.serialno}"
class Reservations(models.Model):
id = models.AutoField(primary_key=True)
active = models.BooleanField(default=True)
name = models.CharField(max_length=30, null=False)
startdate = models.DateField(null=False)
enddate = models.DateField(null=False)
resource = models.ManyToManyField("myapp.Resource", db_table="myapp_resource_reservations", related_name="reservations")
def __str__(self):
return f"{self.name}/{self.startdate}/{self.enddate}"
For example, below are the data present in the models
Resource(format: name/serialno)
>>> Resource.objects.all()
<QuerySet [<Resource: Resource1/RES1>, <Resource: Resource2/RES2>, <Resource: Resource3/RES3>, <Resource: Resource4/RES4>]>
>>>
Reservations(format: name/startdate/enddate/active)
All reservations are made for Resource1
>>> Reservations.objects.all()
<QuerySet [<Reservations: Booking1/2023-03-01/2023-03-07/True>, <Reservations: Booking2/2023-03-15/2023-03-22/True>, <Reservations: BookingX/2023-03-08/2023-03-14/False>]>
>>>
I am trying to retrieve all resources that do not have an 'active' reservation for a given date period using below query.
>>> Resource.objects.exclude((Q(reservations__startdate__range=('2023-03-08','2023-03-14')) | Q(reservations__enddate__range=('2023-03-08','2023-03-14'))) & Q(reservations__active=True))
<QuerySet [<Resource: Resource2/RES2>, <Resource: Resource3/RES3>, <Resource: Resource4/RES4>]>
>>>
Resource1 does have a reservation: BookingX for period 2023-03-08 to 14 but it is active=False. I expected 'Resource1' to show up in above exclude query but it didn't (intended logic: 'exclude all resources that fall in the date range with an active=True reservation').
Can someone help understand why the results are not as expected ? What am I doing wrong ?
Tried using 'filter' instead of 'exclude', it behaves as expected.
>>> Resource.objects.filter((Q(reservations__startdate__range=('2023-03-08','2023-03-14')) | Q(reservations__enddate__range=('2023-03-08','2023-03-14'))) & Q(reservations__active=True))
<QuerySet []>
>>>
>>> Resource.objects.filter((Q(reservations__startdate__range=('2023-03-08','2023-03-14')) | Q(reservations__enddate__range=('2023-03-08','2023-03-14'))) & Q(reservations__active=False))
<QuerySet [<Resource: Resource1/RES1>]>
>>>
You're basically telling the ORM to exclude all Resource's that have ANY active reservations. Because & is evaluated first (before |) it doesn't really matter what is in the ranges as it has already excluded Resource 1 at that point.

Is there such a thing as a conditional "ON DELETE CASCADE" in Django?

Is there a simple way, using on_delete, to delete the object containing a ForeignKey when that ForeignKey is deleted (like models.CASCADE would normally do) but just the ForeignKey relationship to None if some condition is met (like models.SET_NULL would normally do).
Here is some code:
class SomeThing(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
related_name="things",
on_delete=models.CONDITIONAL_CASCADE, # obviously this is wrong
)
is_private = models.BooleanField(default=False)
>>> user = User(username="bob").save()
>>> public_thing = SomeThing(is_private=False)
>>> private_thing = SomeThing(is_private=True)
>>> user.things.add(public_thing)
>>> user.things.add(private_thing)
>>> user.delete()
When user is deleted I would like private_thing to be deleted along w/ user, but public_thing to not be deleted and for public_thing.user to be set to None.
Thanks.
edit:
Thanks to #Jarad's suggestion, I wound up writing the following generic function:
def CONDITIONAL_CASCADE(collector, field, sub_objs, using, **kwargs):
condition = kwargs.get("condition", {})
default_value = kwargs.get("default_value", None)
sub_objs_to_cascade = sub_objs.filter(**condition)
sub_objs_to_set = sub_objs.exclude(**condition)
models.CASCADE(collector, field, sub_objs_to_cascade, using)
collector.add_field_update(field, default_value, sub_objs_to_set)
and using it like this:
CASCADE_PRIVATE_THINGS_ONLY = partial(
CONDITIONAL_CASCADE,
condition={"is_private": True},
default_value=None
)

Why is my Django filter query using dates not returning expected results?

I'm using Django 2.2.6.
I have a model like this:
class NetflowRecord(models.Model):
#############
# Help Text #
#############
min_mac_length_error_message = "Must be at least 12 characters long"
data_help_text = "Bytes"
direction_help_text = "True if record reflects downloaded bytes.<br>False if data reflects uploaded bytes."
##########
# Fields #
##########
timestamp = models.DateTimeField(auto_now_add=True, blank=False, null=False, editable=False)
mac = models.CharField(max_length=17, unique=False, blank=False, null=False, validators=[MinLengthValidator(12, min_mac_length_error_message), validate_mac], editable=False)
data = models.BigIntegerField(default=0, blank=False, null=False, help_text=data_help_text, validators=[MinValueValidator(0)], editable=False)
direction = models.BooleanField(blank=False, null=False, help_text=direction_help_text, editable=False)
site = models.ForeignKey(Site, on_delete=models.CASCADE, blank=False, null=False, related_name="netflow_records")
##########
# Extras #
##########
class Meta:
verbose_name = "Netflow Record"
verbose_name_plural = "Netflow Records"
ordering = ['mac', 'timestamp']
def __str__(self):
formatted_date = self.timestamp.astimezone().strftime("%b %d, %Y - %I:%M:%S %p")
if self.direction:
return f"Download record for {self.mac} on {formatted_date}"
else:
return f"Upload record for {self.mac} on {formatted_date}"
Seems pretty basic.
I have a bunch of records in the database in the table this model created.
Using the interactive shell, if I do this:
>>> from django.utils import timezone
>>> from my_app.models import NetflowRecord
>>>
>>> for record in NetflowRecord.objects.filter()[:3]:
... print(record.timestamp)
...
2020-02-06 19:20:41.758768+00:00
2020-02-06 20:20:46.491537+00:00
2020-02-06 19:07:57.482944+00:00
>>> for record in NetflowRecord.objects.filter(timestamp__year=2020)[:3]:
... print(record.timestamp)
...
2020-02-06 19:20:41.758768+00:00
2020-02-06 20:20:46.491537+00:00
2020-02-06 19:07:57.482944+00:00
>>> for record in NetflowRecord.objects.filter(timestamp__month=2)[:3]:
... print(record.timestamp)
...
# Returned None
>>> for record in NetflowRecord.objects.filter(timestamp__day=6)[:3]:
... print(record.timestamp)
...
# Returned None
>>> today = timezone.now().date()
>>> for record in NetflowRecord.objects.filter(timestamp__date=today)[:3]:
... print(record.timestamp)
...
# Returned None
Why is my filter not working when I used __day, __month, or __date?
The Django documentation says they should all work, and if I start a brand new project using the same Django version, I can't replicate the problem - they all work.
Did I screw something up somewhere?
Edit: I was able to replicate it using this minimal code: https://github.com/terminator14/django_filter_problem.git
Just clone it, install dependencies with pip install -r requirements.txt, edit db.cnf to point to your MySQL database, and run ./manage.py migrate; ./manage.py shell, and do something like:
>>> from django.utils import timezone
>>> from test_app.models import NetflowRecord
>>>
>>> for i in range(3):
... NetflowRecord.objects.create(timestamp=timezone.now())
...
>>> NetflowRecord.objects.filter(timestamp__day=timezone.now().day)
Edit 2: Installing the latest Django 2 available on PIP (2.2.10) didn't fix the problem. Installing the latest Django 3 available on PIP (3.0.3) DID fix the problem. Is this a bug? Or did Django change the way something works?
For any query Django runs, you can see exactly what MySQL commands it will run by adding .query.
This let me do this:
str(NetflowRecord.objects.filter(timestamp__day=6).query)
which showed me a long MySQL command. The important part was this:
... WHERE EXTRACT(DAY FROM CONVERT_TZ(`test_app_netflowrecord`.`timestamp`, 'UTC', 'America/Edmonton')) = 6;
Doing some MySQL queries manually, it turned out if I run:
SELECT CONVERT_TZ('2004-01-01 12:00:00','GMT','MET');
as shown here, it's supposed to give me a date, but it was giving me null. As explained here, this is because MySQL didn't have definitions for timezones loaded.
After running the command from that link (mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql), now the sample code above with GMT to MET works, and my queries work too.

Django Models: error when using DateField as ForeignKey

Having an issue when trying to use DateField of a model class as the ForeignKey for another model class, and using default set to today on both classes. The error message is:
django.core.exceptions.ValidationError: ["'self.date' value has an invalid date format. It must be in YYYY-MM-DD format."]
code:
class DailyImage(models.Model):
date = models.DateField(auto_now_add=True, unique=True)
name = models.CharField(max_length = 1000)
image = models.CharField(max_length = 1000)
location = models.CharField(max_length = 1000)
def __str__(self):
return str(self.id) + ": " + self.name + ", " + self.location
class JournalEntry(models.Model):
date = models.DateField(auto_now_add=True)
journal = models.CharField(max_length = 5000)
image = models.ForeignKey(DailyImage, to_field='date', default='self.date')
The site is a daily journal. Each day, it adds an image from unsplash.it to the DailyImage class, which is then displayed as the header on the home page, and header on the page for the journal entry created that day. When a journal entry is created, it should automatically be referenced to the image that was created that day.
testing it in shell, the date fields seem to match, but are formatted as: datetime.date(YYYY, MM, DD)
>>> a = JournalEntry.objects.get(pk=1)
>>> a
<JournalEntry: test>
>>> a.date
datetime.date(2016, 11, 7)
>>> from journal.models import DailyImage as image
>>> b = image.objects.get(pk=1)
>>> b.date
datetime.date(2016, 11, 7)
>>> b.date == a.date
True
Any suggestions to how this should be done properly would be greatly appreciated!
a.date returns the datetime object, but you have to set the format.
t = datetime.date(2016, 11, 7)
t.strftime("%Y-%m-%d")
# '2016-11-07'
You could also set the default datetime format in settings.py
DATETIME_FORMAT = 'Y-m-d'
However I'm not sure that would be a solution in your situation.

filter from an object and more object that related in django?

This Is my models.py
class Customer(models.Model):
def __unicode__(self):
return self.name
name = models.CharField(max_length=200)
type = models.ForeignKey(Customer_Type)
active = models.BooleanField(default=True)
class Sale(models.Model):
def __unicode__(self):
return "Sale %s (%i)" % (self.type, self.id)
customer = models.ForeignKey(Customer)
total = models.DecimalField(max_digits=15, decimal_places=3)
class Unitary_Sale(models.Model):
book = models.ForeignKey(Book)
quantity = models.IntegerField()
unit_price = models.DecimalField(max_digits=15, decimal_places=3)
sale = models.ForeignKey(Sale)
Views.py
def get_filter_result(self, customer_type='' volume_sale=''):
qdict = {}
if customer_type != '':
qdict['type__name'] = customer_type
qdict['active']=True
#WHAT I AM GOING TO DO NEXT
*** if volume_sale != '':
pass # This point I am asking :)
#IT RETURN CUSTOMERS BASE ON PARAMS.
queryset = Customer.objects.filter(**qdict)
***The volume_sale is:
units=Unitary_Sale.objects.all()
>>> units=Unitary_Sale.objects.all()
>>> for unit in units:
... print unit.sale.customer
... print unit.book,unit.sale.total
...
Sok nara
Khmer Empire (H001) 38.4
Sok nara
killing field (H001) 16
San ta
khmer krom (H001) 20
San ta
Khmer Empire (H001) 20
>>>
{<Customer: Sok nara>: Decimal("56.4"), <Customer: san ta>: Decimal("40")}
Decimal("56.4") , Decimal("40") this is the volume_sale
I could not find the ways to make the filter from difference object as in my case.
It will be great if everyone here help in this stuck? Thanks.
Cool, this actually pretty easy to implement. I used the django annotation feature documented here and here:
from django.db.models import Sum
query = Customer.objects.all().annotate(volume_sale = Sum('Sale__total'))
query.filter(volume_sale < 12.0) #return all customers that have purchased less than 12.0
query[0].volume_sale #you can even get it on the Customer objects given back
Django will take care of the database joins for you. It will put this extra field into each instance of the model that you can filter, order_by and access in templates or views.