Python Serialization (to JSON) issue - python

I'm a bit of a newbie in Python, so go easy on me. I'm writing an AJAX handler in Django. Everything's been pretty straight-forward on this until now. I've been banging my head against this bit for the better part of a day. I'd like to return a JSON string that contains a dict that contains a queryset:
#
# models.py
#
class Project(models.Model):
unique_name = models.CharField(max_length=32, unique=True)
title = models.CharField(max_length=255, blank=True)
description = models.TextField('project description', blank=True)
project_date = models.DateField('Project completion date')
published = models.BooleanField()
class ProjectImage(models.Model):
project = models.ForeignKey('Project', related_name='images')
image = models.ImageField(upload_to=get_image_path)
title = models.CharField(max_length=255)
sort_metric = models.IntegerField()
#
# views.py
#
...
projects = Project.Project.objects.filter(published=True)
...
response_dict({
'success' : True,
'maxGroups' : 5, # the result of some analysis on the projects queryset
'projects' : projects
});
# This one works if I remove 'projects' from the dict
# response = json.dumps( response_dict )
# This one works only on projects
# response = serializers.serialize( 'json', response_dict, relations=('images') )
return HttpResponse( response, mimetype='application/javascript' )
I've commented out the two serialization lines, because:
The first one seems to only work with 'simple' dicts and since projects is included in my dict, it fails with [<Project: Project object>] is not JSON serializable
The second one seems to only work with querysets/models and since the 'outer' part of my dict is non-model, it complains that 'str' object has no attribute '_meta'. Note that I am using the wadofstuff serializer with the understanding that it would resolve the OneToMany relationship as defined in my model. But even when I get this working by only serializing projects, I do not have any of my ProjectImages in the output.
QUESTION 1: What is the best way to serialize the whole response_dict? Surely, I'm not the first person to want to do this, right?
QUESTION 2: Why am I unable to get the ManyToOne relationship to work?
Many thanks for your help.
UPDATE: Just found this one: Django JSON Serialization with Mixed Django models and a Dictionary and it looked promising, but I get 'QuerySet' object has no attribute '_meta' =(

You can't serialize a python object like that. There is a section in the django documentation on what to do.
https://docs.djangoproject.com/en/dev/topics/serialization/#id2
Here is the key part to look at:
json_serializer.serialize(queryset, ensure_ascii=False, stream=response)

Related

Django admin site callable field seems not working with python regex

I'm adding an additional column repath to the Django.admin site, according to the document, it oughts to work, but once I introduced re package to this function, things went wrong.
here's code sample:
class InfoAddrAdmin(ModelAdmin):
list_display = ('id', 'machineId', 'uptime', 'repath')
list_filter = ('machineId',)
def repath(self, obj):
res = re.search(r"10\.1\d+\.\d+\.\d+", obj.ipaddr)
return res.group()<-Exception comes from here
and the related Model is defined as:
class RemoteAddress(models.Model):
id = models.AutoField(primary_key=True)
machineId = models.CharField(max_length=32)
uptime = models.DateTimeField(default=now)
ipaddr = models.TextField()
A piece of text with lots of IP addresses(i.e. the result of command ipconfig) is stored in the ipaddr field, and that regex searchs for an IP with prefix like '10.1xx.xxx.xxx', which proved working fine.
The main error is:
AttributeError: 'NoneType' object has no attribute 'group'
Using debuging tools, the res is not a None, Just the opposite, it contains the correct value, I'm confused why django believes it is None.
Complete Error stack is avaliable at https://pastebin.com/8WUBdg1F
You may find some information from https://docs.djangoproject.com/en/3.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display

Stop affecting other objects of Django Many to Many model

I'm trying to replicate Blood Group as Model as defined in this picture.
.
In my models.py file I had my code to replicate the blood groups like this
class BloodGroup(models.Model):
name = models.CharField(
max_length=3
)
gives = models.ManyToManyField("self")
receives = models.ManyToManyField("self")
def __str__(self):
return self.name
And in my admin.py file I had registered the model as follows
class BloodGroupAdmin(admin.ModelAdmin):
model = BloodGroup
list_display = ['name', 'get_gives', 'get_receives']
def get_gives(self, obj):
return ", ".join([item.name for item in obj.gives.all()])
def get_receives(self, obj):
return ", ".join([item.name for item in obj.receives.all()])
admin.site.register(BloodGroup, BloodGroupAdmin)
Initially I created plain BloodGroup objects without their gives and receives attribute by providing just their names alone. Thus I create an object for all 8 types. Then as I added relationships to each object I found that adding gives or receives for one object affects other objects gives and receives too, making it impossible to replicate the structure in image.
How do I define relationships, without affecting other objects?
In my admin site, I see field names as "get_gives" and "get_receives". How would i make the admin page show field names as "gives" and "receives" but still displaying objects as strings like the image below?
For first question, probably it is better to have only one relation gives. receives can be found from the reverse query. Like this:
class BloodGroup(models.Model):
name = models.CharField(
max_length=3
)
gives = models.ManyToManyField("self", related_name="receives", symmetrical=False)
Then you only need to add objects to gives. receives will be generated automatically.
For second question, add short_description attribute to function(reference to docs). Like this:
get_gives.short_description = 'Gives'
get_receives.short_description = 'Receives'

Django restframework Error binding parameter

When trying to save the data in the DB I have this error:
sqlite3.InterfaceError: Error binding parameter 1 - probably unsupported type.
models.py
class Movie(Model):
title = CharField(max_length=255)
omdb = JSONField()
slug = SlugField(max_length=255, unique=True, allow_unicode=True)
views.py
omdb_data = get_movie(title) # returns response.json() from external API call
print(type(omdb_data['Title'])) # str
print(type(omdb_data)) # dict
movie = Movie(title=omdb_data['Title'],
omdb=omdb_data, slug=slugify(title))
movie.save() # crashing here
What could be wrong? I'm guess it's problem with title or omdb parameters (not sure if ID counts or not) but no idea whats wrong.
SQLite doesn't support all types data. It's in its name (Lite). You may try to convert to PostgreSQL or another complete database solution. Here is a tutorial for Django+Postgres but be careful, it's little bit outdated.

Why GenericRelation fields does not work in Data Migrations(Django)

I want to make data migration in order to add user read post in database. There is such code:
def user_read_posts(apps, schema_editor):
User = apps.get_model("main", "User")
Post = apps.get_model("main", "Post")
Comment = apps.get_model("comments", "Comment")
comments = Comment.objects.all()
for comment in comments:
print (comment.content_object.__class__.__name__)
if isinstance(comment.content_object, Post):
comment.user.read_posts.add(comment.content_object)
class Migration(migrations.Migration):
dependencies = [
('main', '0039_auto_20160314_0906'),
]
operations = [
migrations.RunPython(user_read_posts),
]
And in line print (comment.content_object.__class__.__name__) django raise error:
AttributeError: 'Comment' object has no attribute 'content_object'
Comment model:
class GuidaComment(GenericRelationModel):
user = models.ForeignKey(GuidaUser)
text = models.TextField()
So what should I do?
Thanks.
In RunPython migration scripts apps.get_models() will get historical version of models, not the latest django models you have in source. These historical models are quite limited. Excerpt from django documentation:
historical models will not have any custom methods that you have
defined. They will, however, have the same fields, relationships,
managers (limited to those with use_in_migrations = True) and Meta
options (also versioned, so they may be different from your current
ones).
But that does not mean that you can't use latest and fully functional models by just importing them and using them. In that case you are risking that the migration script won't run correctly in the future - since latest version of model can change (e.g. model method can be renamed, deleted, logic changed etc.). So, it is possible but you need to be aware of the risk using models like this.
Content types framework is pretty old and rarely changed django contrib application and IMHO it is safe enough to use it like this.
Inspired by the answer and solution I made and use, I have made following draft I hope you will find useful:
def user_read_posts(apps, schema_editor):
User = apps.get_model("main", "User")
Post = apps.get_model("main", "Post")
Comment = apps.get_model("comments", "Comment")
from django.contrib.contenttypes.models import ContentType
comments = Comment.objects.all()
for comment in comments:
try:
# assuming that Comment has object_id field holding reference to PK
# of the referenced object
ct = ContentType.objects.get(model=comment.content_type.model,
app_label=comment.content_type.app_label)
content_object = ct.get_object_for_this_type(pk=comment.object_id)
except Exception, ex:
# TODO: can happen if some content type / model is deleted.
continue
print (content_object.__class__.__name__)
if isinstance(content_object, Post):
# TODO: maybe you will need to copy/adapt read_posts.add method's logic here
comment.user.read_posts.add(content_object)
Instead of importing ContentType as suggested in the other answer, I would use apps.get_model to get the historic model, as mentioned here.
Here's what that would look like for the OP's example:
def user_read_posts(apps, schema_editor):
User = apps.get_model("main", "User")
Post = apps.get_model("main", "Post")
Comment = apps.get_model("comments", "Comment")
for comment in Comment.objects.all():
# get historic model for the specified content_type
model = apps.get_model(
app_label=comment.content_type.app_label,
model_name=comment.content_type.model,
)
# get the object with the specified id
content_object = model.objects.get(id=comment.object_id)
if isinstance(content_object, Post):
comment.user.read_posts.add(content_object)
...
This assumes Comment has content_type and object_id fields for the generic relation, as in the generic-relations example.
Exception handling has been omitted for clarity.

How to post to a Django REST Framework API with Related Models

I have two related models (Events + Locations) with a serialzer shown below:
class Locations
title = models.CharField(max_length=250)
address = model.CharField(max_length=250)
class Events
title = models.CharField(max_length=250)
locations = models.ForeignKey(Locations, related_name='events'
class EventsSerializer(serializers.ModelSerializer):
class Meta:
model = Events
depth = 1
I set the depth to 1 in the serializer so I can get the information from the Locations model instead of a single id. When doing this however, I cant post to events with the location info. I can only perform a post with the title attribute. If I remove the depth option in the serializer, I can perform the post with both the title and location id.
I tried to create a second serializer (EventsSerialzerB) without the depth field with the intention of using the first one as a read-only response, however when I created a second serializer, viewset, and added it to the router, it would automatically override the original viewset.
Is it possible for me to create a serializer that outputs the related model fields, and allows you to post directly to the single model?
// EDIT - Here's what I'm trying to post
$scope.doClick = function (event) {
var test_data = {
title: 'Event Test',
content: 'Some test content here',
location: 2,
date: '2014-12-16T11:00:00Z'
}
// $resource.save() doesn't work?
$http.post('/api/events/', test_data).
success(function(data, status, headers, config) {
console.log('sucess', status);
}).
error(function(data, status, headers, config) {
console.log('error', status);
});
}
So when the serializers are flat, I can post all of these fields. The location field is the id of a location from the related Locations table. When they are nested, I can't include the location field in the test data.
By setting the depth option on the serializer, you are telling it to make any relation nested instead of flat. For the most part, nested serializers should be considered read-only by default, as they are buggy in Django REST Framework 2.4 and there are better ways to handle them in 3.0.
It sounds like you want a nested representation when reading, but a flat representation when writing. While this isn't recommended, as it means GET requests don't match PUT requests, it is possible to do this in a way to makes everyone happy.
In Django REST Framework 3.0, you can try the following to get what you want:
class LocationsSerializer(serializers.ModelSerializer):
class Meta:
model = Locations
fields = ('title', 'address', )
class EventsSerializer(serializers.ModelSerializer):
locations = LocationsSerializer(read_only=True)
class Meta:
model = Events
fields = ('locations', )
class EventViewSet(viewsets.ModelViewSet):
queryet = Event.objects.all()
serializer_class = EventsSerializer
def perform_create(self, serializer):
serializer.save(locations=self.request.data['locations'])
def perform_update(self, serializer):
serializer.save(locations=self.request.data['locations'])
A new LocationsSerializer was created, which will handle the read-only nested representation of the Locations object. By overriding perform_create and perform_update, we can pass in the location id that was passed in with the request body, so the location can still be updated.
Also, you should avoid having model names being plurals. It's confusing when Events.locations is a single location, even though Locations.events is a list of events for the location. Event.location and Location.events reads a bit more clearly, the Django admin will display them reasonably, and your fellow developers will be able to easily understand how the relations are set up.

Categories

Resources