Why choices CharField default value also defines DateField default value - python

# models.py
class Url(models.Model):
STATUS_CHOIX = [
('ATTENTE', 'ATTENTE'),
('VALIDE', 'VALIDE'),
('ERREUR', 'ERREUR'),
]
URL = models.URLField(max_length=500)
STATUS = models.CharField(max_length=10, choices=STATUS_CHOIX, default='ATTENTE',)
DATE = models.DateField(auto_now_add=True)
CIBLE = models.CharField(max_length=15)
Here is my model, when I try to save a Url object, I got this strange behaviour, DATE field gets STATUS's default value which is 'ATTENTE' instead of having the record creation date.
I have tried to swap auto_now_add=True with default=date.today() but I get the same problem.
Here is a print screen of what I have in database when I run the following code:
from scrapper.models import Url
a = Url(URL="some_url", CIBLE="some-target")
a.save()

Related

Update a queryset in django

I need to update the data on the database once I get all the values of that data with current DateTime..i.e
My Model:
class Load_Balancing(models.Model):
instance_name = models.CharField(max_length=100)
instance_visit = models.DateTimeField(null=True)
sequence = models.IntegerField()
I want to get the value with the last sequence number inserted in the database and update its time to the current time.
I tried:
instances = Load_Balancing.objects.all().order_by("-sequence")[:1]
a = Load_Balancing.objects.filter(sequence=instances.sequence).update(instance_visit= datetime.datetime.now())
But it's not working.
If you want to update the latest one, you can do that with:
from django.utils import timezone
instance = Load_Balancing.objects.all().latest('sequence')
instance.instance_visit = timezone.now()
instance.save()

What is the "instance" being passed to the to_representation function of my ListSerializer?

The goal of this project is to create an API that refreshes hourly with the most up to date betting odds for a list of games that I'll be scraping hourly from the internet. The goal structure for the JSON returned will be each game as the parent object and the nested children will be the top 1 record for each of linesmakers being scraped by updated date. My understanding is that the best way to accomplish this is to modify the to_representation function within the ListSerializer to return the appropriate queryset.
Because I need the game_id of the parent element to grab the children of the appropriate game, I've attempted to pull the game_id out of the data that gets passed. The issue is that this line looks to be populated correctly when I see what it contains through an exception, but when I let the full code run, I get a list index is out of range exception.
For ex.
class OddsMakerListSerializer(serializers.ListSerializer):
def to_representation(self, data):
game = data.all()[0].game_id
#if I put this here it evaluates to 1 which should run the raw sql below correctly
raise Exception(game)
data = OddsMaker.objects.filter(odds_id__in = RawSQL(''' SELECT o.odds_id
FROM gamesbackend_oddsmaker o
INNER JOIN (
SELECT game_id
, oddsmaker
, max(updated_datetime) as last_updated
FROM gamesbackend_oddsmaker
WHERE game_id = %s
GROUP BY game_id
, oddsmaker
) l on o.game_id = l.game_id
and o.oddsmaker = l.oddsmaker
and o.updated_datetime = l.last_updated
''', [game]))
#if I put this here the data appears to be populated correctly and contain the right data
raise Exception(data)
data = [game for game in data]
return data
Now, if I remove these raise Exceptions, I get the list index is out of range. My initial thought was that there's something else that depends on "data" being returned as a list, so I created the list comprehension snippet, but that doesn't resolve the issue.
So, my question is 1) Is there an easier way to accomplish what I'm going for? I'm not using a postgres backend so distinct on isn't available to me. and 2) If not, its not clear to me what instance is that's being passed in or what is expected to be returned. I've consulted the documentation and it looks as though it expects a dictionary and that might be part of the issue, but again the error message references a list. https://www.django-rest-framework.org/api-guide/serializers/#overriding-serialization-and-deserialization-behavior
I appreciate any help in understanding what is going on here in advance.
Edit:
The rest of the serializers:
class OddsMakerSerializer(serializers.ModelSerializer):
class Meta:
list_serializer_class = OddsMakerListSerializer
model = OddsMaker
fields = ('odds_id','game_id','oddsmaker','home_ml',
'away_ml','home_spread','home_spread_odds',
'away_spread_odds','total','total_over_odds',
'total_under_odds','updated_datetime')
class GameSerializer(serializers.ModelSerializer):
oddsmaker_set = OddsMakerSerializer(many=True, read_only=True)
class Meta:
model = Game
fields = ('game_id','date','sport', 'home_team',
'away_team','home_score', 'away_score',
'home_win','away_win', 'game_completed',
'oddsmaker_set')
models.py:
class Game(models.Model):
game_id = models.AutoField(primary_key=True)
date = models.DateTimeField(null=True)
sport=models.CharField(max_length=256, null=True)
home_team = models.CharField(max_length=256, null=True)
away_team = models.CharField(max_length=256, null=True)
home_score = models.IntegerField(default=0, null=True)
away_score = models.IntegerField(default=0, null=True)
home_win = models.BooleanField(default=0, null=True)
away_win = models.BooleanField(default=0, null=True)
game_completed = models.BooleanField(default=0, null=True)
class OddsMaker(models.Model):
odds_id = models.AutoField(primary_key=True)
game = models.ForeignKey('Game', on_delete = models.CASCADE)
oddsmaker = models.CharField(max_length=256)
home_ml = models.IntegerField(default=999999)
away_ml = models.IntegerField(default=999999)
home_spread = models.FloatField(default=999)
home_spread_odds = models.IntegerField(default=9999)
away_spread_odds = models.IntegerField(default=9999)
total = models.FloatField(default=999)
total_over_odds = models.IntegerField(default=999)
total_under_odds = models.IntegerField(default=999)
updated_datetime = models.DateTimeField(auto_now=True)
views.py:
class GameView(viewsets.ModelViewSet):
queryset = Game.objects.all()
serializer_class = GameSerializer
Thanks
To answer the question in the title:
The instance being passed to the Serializer.to_representation() is the instance you pass when initializing the serializer
queryset = MyModel.objects.all()
Serializer(queryset, many=True)
instance = MyModel.objects.all().first()
Serializer(data)
Usually you don't have to inherit from ListSerializer per se. You can inherit from BaseSerializer and whenever you pass many=True during initialization, it will automatically 'becomeaListSerializer`. You can see this in action here
To answer your problem
from django.db.models import Max
class OddsMakerListSerializer(serializers.ListSerializer):
def to_representation(self, data): # data passed is a queryset of oddsmaker
# Do your filtering here
latest_date = data.aggregate(
latest_date=Max('updated_datetime')
).get('latest_date').date()
latest_records = data.filter(
updated_date_time__year=latest_date.year,
updated_date_time__month=latest_date.month,
updated_date_time__day=latest_date.day
)
return super().to_representation(latest_records)

How do I get my Django query to only return an average and another column?

I'm using Django and Python 3.7. I have these models ...
class Article(models.Model):
website = models.ForeignKey(Website, on_delete=models.CASCADE, related_name='articlesite')
title = models.TextField(default='', null=False)
path = models.TextField(default='', null=False)
url = models.TextField(default='', null=False)
created_on = models.DateTimeField(db_index=True, default=datetime.now)
class ArticleStat(models.Model):
objects = ArticleStatManager()
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='articlestats')
elapsed_time_in_seconds = models.IntegerField(default=0, null=False)
hits = models.FloatField(default=0, null=False)
I want to write a Django query that returns two columns of data -- an elapsed_time_in_seconds (from the ArticleStat model above) and an average of the hits for this particular interval. There are a couple of other constraints in the mix. I have no idea how to do this. I tried the below
qset = ArticleStat.objects.annotate(avg_score=Avg(F("hits")),
hour=(Func(
Func(
F("article__created_on"), function='HOUR FROM'),
function='EXTRACT'))).filter(
article__website=website,
hour=hour)
but this results in the error, "TypeError: expected string or bytes-like object". I'm confused about how to specify the "GROUP BY" part of my SQL statement, and then I'm confused about how to return the two columns of data I want, since I think the above only returns ArticleStat objects, which wouldn't include my average in there.
You need to set an output field on the hour annotation otherwise Django doesn't know you've converted the original date type (created_on) into a number type (hour), and expects to be passed a date string (or a date object, which it confusingly doesn't say however). If you pass a date as hour to filter(), you'll see that the TypeError goes away (but you'll get a ProgrammingError from the database).
So given that you extract the hour as an integer, how about using an IntegerField:
from django.db.models import IntegerField
qset = ArticleStat.objects.annotate(
avg_score=Avg(F("hits")),
hour=(Func(
Func(F("article__created_on"), function='HOUR FROM'),
function='EXTRACT',
output_field=IntegerField()
))
)

Django choices in Admin when Enum is used

I have a model where I am using Enum for choices:
class Agreement(models.Model):
class Category(enum.Enum):
EULA = 0
PROVIDER = 1
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
category = models.IntegerField(
choices=[(choice.name, choice.value)
for choice in Category])
title = models.CharField(max_length=128)
content = models.TextField()
I register it using simple admin site registration:
admin.site.register(Agreement)
When admin site renders the object it doesn't allow me to save it? Has anyone had a similar issue?
According to the documentation:
The first element in each tuple is the actual value to be set on the model, and the second element is the human-readable name.
name and value should be the other way around, like this:
category = models.IntegerField(
choices=[(choice.value, choice.name)
for choice in Category])
because category is an integer field and name returns a string.

Django ORM Issue

I am learning RestAPI and When I try to post data to update my database columns the modified_on column should automatically populated to current date and time but it is not updating.
I am currently using django cassandra engine ORM where there is no functionality like auto_add_now() or auto_now().
Can any one give a suggestion where am I going wrong?
Model Class:
class Mydb(DjangoCassandraModel):
id = columns.UUID(primary_key=True, default=uuid.uuid4())
user_name = columns.Text()
user_email = columns.Text(default=None)
user_password = columns.Text()
description = columns.Text()
creation_date = columns.DateTime(default=datetime.datetime.today(), static=True)
modified_on = columns.DateTime(default=datetime.datetime.today())
My Serialization class:
class TaskSerializer(serializers.Serializer):
# id = serializers.UUIDField(default=uuid.uuid4)
USER_ID = serializers.UUIDField(default= uuid.uuid4(),source='id')
# user_name = serializers.CharField(max_length=50)
USER_NAME_FIELD = serializers.CharField(max_length=50, source='user_name')
USER_EMAIL = serializers.CharField(source='user_email')
USER_PASSWORD = serializers.CharField(max_length=20, source='user_password')
EXPLANATION = serializers.CharField(max_length=100, source='description')
MODIFIED_AT = serializers.DateTimeField(default=datetime.datetime.today(), source='modified_on')
CREATED_ON = serializers.DateTimeField(default=datetime.datetime.today(), source='creation_date')
def create(self, validated_data):
return Mydb.objects.create(**validated_data)
def update(self, instance, validated_data):
# instance.id = validated_data.get('id', instance.id)
instance.user_name = validated_data.get('user_name', instance.user_name)
instance.user_email = validated_data.get('user_email', instance.user_email)
instance.user_password = validated_data.get('user_password', instance.user_password)
instance.description = validated_data.get('description',instance.description)
instance.modified_on = validated_data.get('modified_on', instance.modified_on)
instance.save()
# instance.creation_date = validated_data.get('creation_date', instance.creation_date)
You should rather use utils now for timezone aware times
from django.utils.timezone import now
also in model you should set function not evaluated value ( no parenthesis after now )
MODIFIED_AT = serializers.DateTimeField(default=now, source='modified_on')
MODIFIED_AT = serializers.DateTimeField(default=datetime.datetime.today(), source='modified_on')
to
MODIFIED_ON = serializers.DateField(default=datetime.datetime.today(), source='modified_on')
change MODIFIED_AT to MODIFIED_ON
You can try:
create_date = models.DateField(auto_now_add=True,
verbose_name=u'Create date')
update_date = models.DateTime(auto_now=True,
verbose_name=u'Update date')
auto_now_add automatically set the field to now when the object is first created.
auto_now=True automatically set the field to now every time the object is saved.
Doc is here.
Please make sure to add the auto_now=True for your modified_at filed, in your model.
It automatically sets the field to now every time the object is saved. Useful for “last-modified” timestamps. Note that the current date is always used; it’s not just a default value that you can override.
Example:
class Mydb(DjangoCassandraModel):
creation_date = columns.DateTime(auto_now_add=True)
modified_on = columns.DateTime(auto_now=True)
Docs Here:
https://docs.djangoproject.com/en/1.11/ref/models/fields/#django.db.models.DateField.auto_now
You use default=datetime.datetime.today() as your default value for the fields. Since you call the function immediately (by adding ()), the function is called exactly once on the first load of the code and the datetime at that moment is put into the default value and not updated until you reload the code (a.k.a. restart the server).
If you want to always use the then current time, leave away the () to cause Django to call the function each time. default=datetime.datetime.today
It's preferable for you to use now though, like iklinac did in his answer, as that also respects your timezone settings. His anwer also leaves out the parenteses, yielding the correct result.
from django.utils.timezone import now
...
MODIFIED_AT = serializers.DateTimeField(default=now, source='modified_on')

Categories