WTForms StringField Dynamic Default Value using datetime.utcnow / Callable? - python

I have a WTForm with the following:
class MyForm(FlaskForm):
...
timestamp = DateTimeField("Timestamp", default = datetime.utcnow, validators = [Required()])
name = StringField("Name", default = str(int(datetime.utcnow().timestamp())), validators = [Optional()])
...
Upon creating a new form the default timestamp field updates as expected while the default name field continues to use the timestamp of when the app was started. I can successfully set the timestamp in my route, my preference is to utilize the default functionality of the form. Looking here:
The Field base class
It states that default "May be a callable." but I haven't been able to find a working example of this. I'm assuming if I make this a callable, the default value will update with current timestamp. Does anyone know how to make default get its value from a function?
Thanks in advance,
Brian

In your timestamp field, you provide a callable - datetime.datetime.utcnow to default, so each time the form is instantiated the callable is called and the new result is used.
In your name field, datetime.datetime.utcnow().timestamp() is evaluated when the form is compiled, so the default value is the same for every instance of the form.
If you want the default value of name to be evaluated every time the form is instantiated, pass it a function (a callable) that returns datetime.datetime.utcnow().timestamp().
Like this:
def get_default():
datetime.datetime.utcnow().timestamp()
class MyForm(FlaskForm):
timestamp = DateTimeField("Timestamp", default=datetime.utcnow, validators=[Required()])
name = StringField("Name", default=get_default, validators=[Optional()])
or if you prefer, you can use a lambda:
class MyForm(FlaskForm):
timestamp = DateTimeField("Timestamp", default=datetime.utcnow, validators=[Required()])
name = StringField("Name", default=lambda : datetime.datetime.utcnow().timestamp(), validators=[Optional()])

Related

UUID as default value in Django model

I've noticed the strange behaviour of default value in django model. For example we have a simple django model:
import uuid
...
class SiteUser(models.Model):
...
username = models.CharField(max_length=255, verbose_name=u"Username")
activation_key = models.CharField(max_length=64, verbose_name=u"Activation key", default=uuid.uuid1())
When I create a new user, and after that another one like that:
user_a = SiteUser(username="UserA")
user_a.save()
user_b = SiteUser(username="UserB")
user_b.save()
Django makes 2 users with the same activation_key
But then I do it like that:
user_a = SiteUser(username="UserA")
user_a.activation_key = uuid.uuid1()
user_a.save()
user_b = SiteUser(username="UserB")
user_b.activation_key = uuid.uuid1()
user_b.save()
Everything works fine and Django creates 2 users with different activation keys.
What's going on here? Python loads model object and calculate the default value of the model when the wsgi app starts up or that? Why uuid gives the same values in the first case but different in second?
Thanks.
Problem is the default attribute that you are setting as
activation_key = models.CharField(max_length=64, verbose_name=u"Activation key",
default=uuid.uuid1())
Here you are setting the default value as not a callable but value returned by uuid.uuid1() call when this model class is initialized.
You should set it as default=uuid.uuid1 which sets it as callable, and is sets new uuid each time new default value needs to be used.
As of Django 1.8, there is a new UUIDField available. It's described in the following link which also covers how to set defaults:
https://docs.djangoproject.com/en/1.8/ref/models/fields/#uuidfield

Automate some entries in Django models passing the entry value from same model

This is my models.py code:
from django.db import models
import shortuuid
class website(models.Model):
url = models.URLField(max_length=100)
uid = models.CharField(unique = True, max_length=40,default=str(shortuuid.uuid(name=url)))
def __unicode__(self):
return self.url
In django admin panel the value of uid does not change when I enter a URL.I just want to enter the URL and then want to generate the uid using shortuuid function.I want to set uid as editable=False but before that I want to ensure that the function is woking properly.How to automate the uid value passing url as input?
That's not where you would do it. You can't write something at the class level that depends on an instance attribute of the class: it's simply not possible. And what's more, a default is allocated when the object is instantiated, but you want that to change after the user has changed the value of another attribute, so this isn't a default at all.
Instead you probably want to define this value on save. That's easy to do by simply overriding the save method:
def save(self, *args, **kwargs):
if not self.uid:
self.uid = str(shortuuid.uuid(name=self.user))
return super(website, self).save(*args, **kwargs)

Field in model is 'undefined' inside objects.filter()

In my model, I have a DateField called 'expire_date'. I have a function that queries my model:
def date_delta_days(earlier_date, later_date):
return (later_date - earlier_date).days
class Command(BaseCommand):
help = """ Manages list expiration. If lists are expiring soon, sends an email warning the list owners
If a list has expired, the .pck file associated with that list is deleted
"""
def handle(self, *args, **options):
now = datetime.now()
expiring_lists = ListEntry.objects.filter(
date_delta_days(now, expire_date) # <------ error is on this line
)
When I write this, I get a syntax error claiming that expiration_date is unrecognized even though it is defined as a field in my ListEntry model. Is there something I am missing here? This seems like it would be a trivial query to write in SQL.
Here is my Model definition:
class ListEntry(models.Model):
name = models.CharField(max_length=64)
# active_date = models.DateTimeField('date of last list activity')
expire_date = models.DateField('date of expiration')
create_date = models.DateField('date created')
class Meta:
ordering = ('name',)
Here is the relevant error code:
File "/home/jared/projects/list_expiration/list_app/management/commands/check_lists.py", line 19, in handle
date_delta_days(now, expire_date)
NameError: global name 'expire_date' is not defined
In your filter, you pass a positional argument - probably a timedelta, that is returned from time_delta_days. In that function you use two variables: a datetime object, returned by datetime.datetime.now() and... here's the cause of your problems. You're passing a variable named expiration_date, that is most probably undefined at that point.
What you probably want to do, is to use named arguments, as this is what filter() works with. Because datetime fields are comparable, you can use the __gt and __lt keywords (and other supported logical operators). You want to find ListEntry objects that will expire in less than 30 days from now, then use:
month_from_now = datetime.datetime.now() + datetime.timedelta(days=30)
ListEntry.objects.filter(expiration_date__lt=month_from_now,
expiration_date__gt=datetime.datetime.now())
I've added a second filter argument, just to make sure you're not looking at ListEntry objects that have already expired. You can easily adapt this code to your needs.

Passing date time in a different format to models.DateTimeField in Django?

I have a model that looks something like this
class Post(models.Model):
id = models.IntegerField(unique=True, primary_key=True)
title = models.CharField(max_length=150, blank=True)
created = models.DateTimeField(blank=True)
...
I have to fill the database with chunks of data. I am getting the data in form of a flat json string (no nesting) so my work is pretty easy i.e.
mydict = json.loads(jsonstr)
mypost = Post(**mydict)
mypost.save()
there is just one issue that the date-time is expressed in "YYYY-MM-DDThh:mm:ss+zzzz" format ( e.g. "created" : "2011-11-17T09:21:31+0000" ) which breaks the above code.
I know forms.DateTimeField has input_formats. Is there a way I could make DateTimeField accept above format?
Subclass DateTimeField and convert the value appropriately:
class ConvertingDateTimeField(models.DateTimeField):
def get_prep_value(self, value):
return str(datetime.strptime(value, FORMAT_STRING))
Then use ConvertingDateTimeField to replace models.DateTimeField.
There's probably a better way to do it if you look how DateTimeField creates the datetime object.
You can use a ModelForm and override the created form field instead of going directly to your model.
Something like this:
from django.forms import ModelForm
class PostForm(ModelForm):
created = forms.DateTimeField(input_formats=('%Y-%m-%dT%H:%M:%S+0000',))
class Meta:
model = Post
mydict = ...
form = PostForm(mydict)
if not form.is_valid():
# handle appropriately for your app
raise Exception
instance = form.save() # or use 'commit=False' if you don't want to save to DB
Unfortunately, I don't think strptime supports the GMT offset hence why I've hard-coded it in the input_format. You may be able to override the 'clean' method for that field if you want to scrub it yourself.
I believe the DateTimeField expects a timezone-naive datetime, so you need to strip off the (+zzzz) part.
naive = dt.replace(tzinfo=None)
Applies to Python 2.x only.
ok this is what i did finally :
class MyDateTimeFeild(models.DateTimeField):
def get_prep_value(self, value):
from dateutil.parser import parse
from datetime import timedelta
td = float(value[-5:])/100
timediff = timedelta(hours=td)
return parse(value).replace(tzinfo=None) - timediff
thanks every one!! :) all posts were very helpfull.

Accessing Django custom models through Manager returns empty set

From all I've read, it appears this should Just Work, but it doesn't.
I have a custom model:
from django.db import models
from django.contrib.auth.models import *
class Feed(models.Model):
user = models.ForeignKey(User, blank=True, null=True)
link = models.CharField(max_length=200)
startDate = models.CharField(max_length=8)
endDate = models.CharField(max_length=8)
def __unicode__(self):
return str(self.id)
def __init__(self, link, sDate, eDate, user=None):
super(Feed, self).__init__()
self.link = link
self.startDate = sDate
self.endDate = eDate
self.user = user
And I'm also using the User model included in 'django.contrib.auth.models'.
When I create a feed, e.g.
feed = Feed(link, sDate, eDate)
feed.save()
(or a similar one with a user specified) it appears to store it in the database (I get its PK which keeps incrementing), but 'Feed.objects.all()' returns an empty list. Trying to filter by an existing pk also returns an empty list and trying to get() an existing pk gives me the following error
TypeError: __init__() takes at most 5 arguments (6 given)
Looking at how I should be retrieving objects from custom models, it appears that I've done everything I should, but that is clearly not the case...
Whoa.
Why are you overriding your model's __init__? There are very few good reasons to do this, and if you do, you must absolutely keep the interface the same- because that __init__ is called every time django pulls one of your models from the db (which is why you get the error when you call .get())
What are you hoping to accomplish with your __init__?
You should probably just delete your __init__ and then you can create Feed objects the normal, django way:
feed = Feed(link=link, startDate=sDate, endDate=eDate)
That line will create the correct feed object you want.
Did you try named arguments, e.g.
feed = Feed(link=link, startDate=sDate, endDate=eDate)
How did you use get()? It should also be used with named arguments, e.g.:
Feed.objects.get(pk=6)

Categories

Resources