How to format datetime in a serialized model Django - django

I have the following model
class MyModel(models.Model):
firstDate = models.DateTimeField(auto_now_add=True)
another = models.CharField(max_length=30)
I serialize it to JSON with
query = MyModel.objects.all()
data = serializers.serialize('json', query, use_natural_foreign_keys=True)
The DateTimeField returns the following format:
2017-12-19T22:50:04.328Z
The desired format is:
2017-12-19 22:50:04
Is there a simple way to achieve this without using the queryset.values() and queryset.extra() methods?

You'll want to create your own subclass of DjangoJSONEncoder - which is a subclass of Python's standard JSON encoder that implements (among other things) the functionality that you wish to change.:
If you look at the source code, it's pretty trivial. You'll want to change the section starting with:
if isinstance(o, datetime.datetime):
to have the method return the datetime in the format you desire.
class MyJSONEncoder(DjangoJSONEncoder):
def default(self, o):
if isinstance(o, datetime.datetime):
# Your implementation here
return super().default(o)

As suggested by aircraft, I treat it in the front-end with the JavaScript built-in Date implementation (didn't know about it until now). It's perfect for manipulating formats and locales.
In specific, I used this object function: Date.prototype.toLocaleFormat

Related

Django DRF serializer Custom relational fields How to build return value of to_internal_value?

I'm trying to implement something very similar to Djang Rest Framework tutorial Custom relational fields.
For the reminder, the provided code snippet is:
import time
class TrackListingField(serializers.RelatedField):
def to_representation(self, value):
duration = time.strftime('%M:%S', time.gmtime(value.duration))
return 'Track %d: %s (%s)' % (value.order, value.name, duration)
class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackListingField(many=True)
class Meta:
model = Album
fields = ['album_name', 'artist', 'tracks']
And "This custom field would then serialize to the following representation" (quoted in the tutorial):
{
'album_name': 'Sometimes I Wish We Were an Eagle',
'artist': 'Bill Callahan',
'tracks': [
'Track 1: Jim Cain (04:39)',
'Track 2: Eid Ma Clack Shaw (04:19)',
'Track 3: The Wind and the Dove (04:34)',
...
]
}
I understand that and have implemented it for my particular case.
What I don't understand is the way to implement to_internal_value(self, data) as I want to provide a read-write API.
I understand that to_internal_value(self, data) should return an AlbumTrack object, but I don't understand how to build it. In particular how to get back the Album related id?
If we post the JSON structure above, to_internal_value(self, data) will be called once per track with 'Track 1: Jim Cain (04:39)'... for data values. I don't see how we can update the tracks model from those data values.
It seems that you're trying to implement writable nested serializers. While nested serializers are read-only by default, the DRF has a section that explains how to implement writable ones: https://www.django-rest-framework.org/api-guide/relations/#writable-nested-serializers
Since you want the TrackListingField to serialize the Track model it should inherit from ModelSerializer:
class TrackSerializer(serializers.ModelSerializer):
class Meta:
model = Track
fields = ['order', 'name', 'duration']
You'll then have to override the create method for AlbumSerializer:
def create(self, validated_data):
tracks_data = validated_data.pop('tracks')
album = Album.objects.create(**validated_data)
for track_data in tracks_data:
Track.objects.create(album=album, **track_data)
return album
Please note that the above will make one database query per track. You can make use of Track.objects.bulk_create to make only one query to create all tracks.
To answer your initial question about to_internal_value, you can see what the default is by adding this print statement to the overridden to_internal_value:
class TrackSerializer(serializers.ModelSerializer):
...
def to_internal_value(self, data):
default_return_value = super(TrackSerializer, self).to_internal_value(data)
print(default_return_value)
return default_return_value
In the case of a ModelSerializer the DRF uses an OrderedDict output for to_internal_value. Your custom to_internal_value would have to extract the order, name and duration from the data string using a regex, and put them in an OrderedDict. However in this case it'd probably be easier to use a dictionary as representation for the tracks.

Converting string into datetime within Django modelform

I would like to accept natural language strings into a DateTime field, on a form generated by a Django ModelForm. I found Converting string into datetime, which explains how to convert the input string into a DateTime object. (In this case, I'm using timestring for the conversion, instead of strptime as suggested in the answers, because I want to handle input like 'tomorrow'.) But I can't figure out where code this like should be placed within the ModelForm code. If the conversion code is placed in form_valid, it never gets run, because is_python runs first and complains that the text input is not a DateTime. When I override is_python, I get an error that I think comes from some kind of recursive loop.
Relevant code:
models.py
class Widget(models.Model):
name = models.CharField(max_length=100)
widget_date = models.DateTimeField
forms.py
from timestring import Date
class NaturalDateField(forms.DateField):
def to_python(self, value):
if not value:
return none
return Date(value, tz=timezone.get_current_timezone())
class WidgetForm(forms.ModelForm):
widget_date = NaturalDateField()
class Meta:
model = Widget
fields = ['name', 'widget_date']
views.py
class WidgetUpdate(UpdateView):
model = Widget
form_class = WidgetForm
The error on submit is Invlid date string >>. Tracing the code shows that the initial input string converts correctly (to something like '2014-12-26 00:00:00-08:00'), but then the validate() function from site-packages/django/forms/fields.py runs and that goes back into the timestring package for some reason and tries to run def __eq__(self, other): from Date.py, which I think tries to run Date(other), which fails since other is blank.
What is the best method to accept a text string in a ModelForm and then convert it to a field-specific string such as DateTime to be saved in the database?
Looking at that project, your code will return a timestring.Date object, which Django doesn't know what to do with. You probably just need to get the date value from there:
def to_python(self, value):
if not value:
return none
parsed_date = Date(value, tz=timezone.get_current_timezone())
return parsed_date.date
Try reusing builtin django date parsing methods:
class NaturalDateField(forms.DateField):
def to_python(self, value):
value = super(NaturalDateField, self).to_python(value)
return value.replace(tzinfo=timezone.get_current_timezone())

Passing JSON with Django

I'm passing information to a Processing sketch from a Django backend. The problem arises with "lectures", it seems that it is not serializable. I'm missing something very basic methinks.
def get(request, last_update = None):
timestamp = time.time()
if last_update:
last_update = datetime.fromtimestamp(float(last_update))
lectures = Lecture.objects.all().filter(datetime_edit__gt=last_update)
else:
lectures = Lecture.objects.all()
updates = {'timestamp': timestamp, 'lectures': lectures}
print updates
return HttpResponse(json.dumps(updates), mimetype="application/json")
Here is the response that I'm getting in the browser.
[<Lecture: whales>, <Lecture: cats>, <Lecture: rain>, <Lecture: china>] is not JSON serializable
QuerySet cannot be serialized in this manner. Use .values() and list() to turn it into a basic Python structure (i.e. a list of dicts) first.
The JSON serialize does not support serialization of arbitrary class instances. You should convert Lecture records into dictionaries before serializing.

Can't Return JSON object using MongoEngine Pymongo with Django?

So I'm trying to return a JSON object for a project. I've spent a few hours trying to get Django just returning the JSON.
Heres the view that we've been working with:
def json(request, first_name):
user = User.objects.all()
#user = User.objects.all().values()
result = simplejson.dumps(user, default=json_util.default)
return HttpResponse(result)
Here's my model:
class User(Document):
gender = StringField( choices=['male', 'female', 'Unknown'])
age = IntField()
email = EmailField()
display_name = StringField(max_length=50)
first_name = StringField(max_length=50)
last_name = StringField(max_length=50)
location = StringField(max_length=50)
status = StringField(max_length=50)
hideStatus = BooleanField()
photos = ListField(EmbeddedDocumentField('Photo'))
profile =ListField(EmbeddedDocumentField('ProfileItem'))
allProfile = ListField(EmbeddedDocumentField('ProfileItem')) #only return for your own profile
This is what it's returning:
[<User: User object>, <User: User object>] is not JSON serializable
Any thoughts on how I can just return the JSON?
With MongoEngine 0.8 or greater, objects and querysets have a to_json() method.
>>> User.objects.to_json()
simplejson.dumps() doesn't know how to "reach into" your custom objects; the default function, json_util.default must just be calling str() or repr() on your documents. (Is json_util custom code you've written? If so, showing its source here could prove my claim.)
Ultimately, your default function will need to be able to make sense of the MongoEngine documents. I can think of at least two ways that this might be implemented:
Write a custom default function that works for all MongoEngine documents by introspecting their _fields attribute (though note that the leading underscore means that this is part of the private API/implementation detail of MongoEngine and may be subject to change in future versions)
Have each of your documents implement a as_dict method which returns a dictionary representation of the object. This would work similarly to the to_mongo method provided on documents by MongoEngine, but shouldn't return the _types or _cls fields (again, these are implementation details of MongoEngine).
I'd suggest you go with option #2: the code will be cleaner and easier to read, better encapsulated, and won't require using any private APIs.
As dcrosta suggested you can do something like this, hope that will help you.
Document definition
class MyDocument(Document):
# Your document definition
def to_dict(self):
return mongo_to_dict_helper(self)
helper.py:
from mongoengine import StringField, ListField, IntField, FloatField
def mongo_to_dict_helper(obj):
return_data = []
for field_name in obj._fields:
if field_name in ("id",):
continue
data = obj._data[field_name]
if isinstance(obj._fields[field_name], StringField):
return_data.append((field_name, str(data)))
elif isinstance(obj._fields[field_name], FloatField):
return_data.append((field_name, float(data)))
elif isinstance(obj._fields[field_name], IntField):
return_data.append((field_name, int(data)))
elif isinstance(obj._fields[field_name], ListField):
return_data.append((field_name, data))
else:
# You can define your logic for returning elements
return dict(return_data)

django form datefield with runtime formatting

So, I have a form:
class FormBasicInfo(BasicForm):
valid_from = forms.DateField(required=False, input_formats=('%d/%m/%Y',), widget=DateInput(format='%d/%m/%Y'))
and I set the input and output formats. However, what if I want to set these formats at runtime, based on the date format preference of my user? how can that be done?
The way it is done above, the form will always validate against the European date format. Even if I specify more formats which is allowed, one of them will be first and take priority which means there will be cases when the validation will be done incorrectly.
You can override the __init__ method of the form class to customize the input_formats and widget. For e.g.
class FormBasicInfo(BasicForm):
....
def __init__(self, *args, **kwargs):
super(MForm, self).__init__(*args, **kwargs)
valid_from = self.fields['valid_from']
format = look_up_format_based_on_locale()
valid_from.input_formats = (format,)
valid_from.widget = forms.DateInput(format=format)
Where look_up_format_based_on_locale() is an abstraction for looking up the date format based on the user's locale. It should return an appropriate format string, say "%m/%d/%Y".