Serialize django models with reverse One-To-One fields to JSON - django

Lets say I have the following two django (1.3) models
from django.db import models
class Patient(models.Model):
name = models.CharField('Name', max_length=50)
class Address(models.Model):
town = models.CharField('Town/Village', max_length=50)
patient = models.OneToOneField(Patient, related_name='address')
Now when i try to serialize a Patient model instance to JSON using django's serializers, the resulting JSON string does'nt have the address details with it (it's unable to traverse through the reverse direction of One-to-one relation)
This happens event if I use select_related('address') to populate the address object into the cache.
i.e.
from django.core import serializers
>>> print serializers.serialize('json',[Patient.objects.select_related('address').get(id=1)])
Is there are way I can get around this problem?

This problem came up in the project I am currently developing. Here is the solution we were going to use before we decided to just extend the Django serializer ourselves. This should work just fine for your needs though.
To solve the problem with out getting your hands dirty I would recommend wadofstuff's Django Full Serializers.
After getting it setup using the wiki solving your example would look something like this:
You need to add a method to your model which returns a python representation of the QuerySet.
from django.db import models
from django.core import serializers
class Patient(models.Model):
name = models.CharField('Name', max_length=50)
def extra_address(self):
return serializers.serialize('python', self.address.all())
class Address(models.Model):
town = models.CharField('Town/Village', max_length=50)
patient = models.OneToOneField(Patient, related_name='address')
Then you can add the method to the list of extras taken by the more robust serializer.
from django.core import serializers
print serializers.serialize('json', Patient.objects.all(), extras=('extra_address',))
An extras key will be added to the dictionary and will have whatever extra pieces of information you needed in it.

Related

Django-Oscar form fork - Error unknown fields (but fields are in the models)

I am trying to fork django-oscar to change the dashboard form for product attributes, multioption. Need to have a description field for every option.
project/oscar_fork/catalogue/models.py:
from django.db import models
from django.utils.translation import ugettext_lazy as _
from oscar.apps.catalogue.abstract_models import AbstractAttributeOption, AbstractAttributeOptionGroup
class AbstractAttributeOption(AbstractAttributeOption):
description = models.CharField(_('Description'), max_length=250, blank=True)
group = models.ForeignKey(
'catalogue.AttributeOptionGroup',
on_delete=models.CASCADE,
related_name='optionsblabla',
verbose_name=_("Group"))
from oscar.apps.catalogue.models import *
The models are changed with the extra field "description" in my DB, but still my form field returns cannot find this field.
project/oscar_fork/dashboard/catalogue/forms.py:
from oscar.apps.dashboard.catalogue import forms as base_forms
class AttributeOptionForm(base_forms.AttributeOptionForm):
class Meta(base_forms.AttributeOptionForm.Meta):
fields = ('option', 'description')
If I change the form.py and models.py fields directly in the Oscar app it works. Other forms can be forked that easily as shown above. Tried it with AttributeOptionGroupForm. I think there is a problem with the sequence of import. How can I solve this?
Error:
django.core.exceptions.FieldError: Unknown field(s) (description) specified for AttributeOption
I am using django-oscar v1.6. Django v.2.08.
Your concrete model should be named AttributeOption without the 'Abstract', otherwise oscar will not pick it up and use its own AttributeOption model instead, which has no description:
class AttributeOption(AbstractAttributeOption):
description = ...
You will have to run makemigrations and migrate after that.
Check the source code of the models module that you import at the end. You will see how their dynamic model loading works:
if not is_model_registered('catalogue', 'AttributeOption'):
class AttributeOption(AbstractAttributeOption):
pass

Django (1.9) Watson index Django Tagging TagField

How can I index a field that is managed by the Django Tagging (v0.4.5) TagField manager?
The tags are all working correctly and Watson (v1.2.1) is indexing the models and returning results from searching the char and text fields as it should but not if the search term is a tag.
The registering is done in an AppConfig as documented:
from __future__ import unicode_literals
from django.apps import AppConfig
from watson import search as watson
class TeamConfig(AppConfig):
name = 'team'
def ready(self):
Team = self.get_model("Team")
watson.register(Team, fields=("title_text", "tagline", "description", "tags"))
Member = self.get_model("Member")
watson.register(Member)
and the Team model that has the tag TagField field is all good:
import blahs
...
from watson import search as watson
from tagging.fields import TagField
...
class Team(models.Model):
pub_date = models.DateField('date published', auto_now_add=True)
title_text = models.CharField('Name', max_length=200, blank=False,
...
tags = TagField()
is_active = models.BooleanField('Active?', default=True)
Anyone got any idea how to get the field indexing same as a char or text field please?
Thanks so much
Rich
In the end I ripped out Django Tagging, created my own model of 'tags' simply with a date and a name string and then threw a ManyToMany field in each of the models to be tagged. Needs some extra logic to update that pool of tags but that seems easy enough with a loop to test if tag.name.exists() upon save for the associated models.
Having that simple field type also made Django Material form way easy to add a selector for the static pool of tags too so win win.
Would still like to know if anyone has a way of returning text through a model manager as I've only been learning python and django on/off for a few months and really loving it so want to do things proper like.

Django model managers.py and models.py

given the following situation:
models.py
from .managers import PersonManager
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=256)
last_name = models.CharField(max_length=256)
managers.py
from .models import Person
from django.db import managers
class PersonManager(models.Manager):
def create(self, person_dict):
new_person = Person(
first_name=person_dict['first_name']
last_name=person_dict['last_name'])
new_person.save()
How can I write my model manager to avoid circular import?
It is actually not working, my guess is that I would have to create my object inside my manager without refering to it as class Person, instead I should use a more general generic Django name. Any thoughts?
There are a few options here.
Firstly, you could define the model and the manager in the same file; Python has no requirement or expectation that each class is in its own file.
Secondly, you don't actually need to import the model into the manager. Managers belong to models, not the other way round; from within the manager, you can refer to the model class via self.model.
And finally, if that's all your manager is doing, there is no reason for it at all. Managers already have a create method; it takes keyword parameters, rather than a dict, but that just means you can call it with Person.objects.create(**person_dict).

Extending User profile in Django 1.7

I know, this question has been already asked many times in SO, but most of the answers I read were either outdated (advising the now deprecated AUTH__PROFILE_MODULE method), or were lacking of a concrete example.
So, I read the Django documentation [1,2], but I lack a real example on how to use it properly.
In fact, my problem comes when a new user is created (or updated) through a form. The user is obviously created but, the fields from the extension are all unset. I know that the Django documentation is stating that:
These profile models are not special in any way - they are just Django models that happen to have a one-to-one link with a User model. As such, they do not get auto created when a user is created, but a django.db.models.signals.post_save could be used to create or update related models as appropriate.
But, I don't know how to do it in practice (should I add a a receiver and if 'yes', which one).
For now, I have the following (taken from the documentation for the sake of brevity):
File models.py
from django.contrib.auth.models import User
class Employee(models.Model):
user = models.OneToOneField(User)
department = models.CharField(max_length=100)
File admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from my_user_profile_app.models import Employee
# Define an inline admin descriptor for Employee model
class EmployeeInline(admin.StackedInline):
model = Employee
can_delete = False
verbose_name_plural = 'employee'
# Define a new User admin
class UserAdmin(UserAdmin):
inlines = (EmployeeInline, )
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
File forms.py
class SignupForm(account.forms.SignupForm):
department = forms.CharField(label="Department", max_length=100)
class SettingsForm(account.forms.SignupForm):
department = forms.CharField(label="Department", max_length=100)
Then, in my code, I use it like this:
u = User.objects.get(username='fsmith')
freds_department = u.employee.department
But, Signup and Settings forms do not operates as expected and new values for the departement is not recorded.
Any hint is welcome !
I have looked at all the answers but none does really hold the solution for my problem (though some of you gave me quite good hints for looking in the right direction). I will summarize here the solution I have found to solve my problem.
First of all, I have to admit I didn't tell everything about my problem. I wanted to insert extra fields in the User model and use other apps such as the default authentication scheme of Django. So, extending the default User by inheritance and setting AUTH_USER_MODEL was a problem because the other Django applications were stopping to work properly (I believe they didn't use user = models.OneToOneField(settings.AUTH_USER_MODEL) but user = models.OneToOneField(User)).
As, it would have been too long to rewrite properly the other applications I am using, I decided to add this extra field through a One-to-One field. But, the documentation miss several points that I would like to fill in the following.
So, here is a complete example of adding an extra field to the User model with other applications using the same model.
First, write the description of the model gathering the extra fields that you want to add to your models.py file:
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User)
extra_field = models.CharField(max_length=100)
Then, we need to trigger the addition of an object UserProfile each time a User is created. This is done through attaching this code to the proper signal in the receiver.py file:
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
from my_user_profile_app.models import UserProfile
#receiver(post_save, sender=User)
def handle_user_save(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
Now, if you want to be able to modify it through the administration interface, just stack it with the usual UserAdmin form in the admin.py file.
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from my_user_profile_app.models import UserProfile
# Define an inline admin descriptor for UserProfile model
class UserProfileInline(admin.StackedInline):
model = UserProfile
can_delete = False
# Define a new User admin
class UserAdmin(UserAdmin):
inlines = (UserProfileInline, )
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
Then, it is time now to try to mix this extra field with the default Django authentication application. For this, we need to add an extra field to fill in the SignupForm and the SettingsForm through inheritance in the forms.py file:
import account.forms
from django import forms
class SignupForm(account.forms.SignupForm):
extra_field = forms.CharField(label="Extra Field", max_length=100)
class SettingsForm(account.forms.SignupForm):
extra_field = forms.CharField(label="Extra Field", max_length=100)
And, we also need to add some code to display and get properly the data that you have been added to the original User model. This is done through inheritance onto the SignupView and the SettingsView views in the views.py file:
import account.views
from my_user_profile_app.forms import Settings, SignupForm
from my_user_profile_app.models import UserProfile
class SettingsView(account.views.SettingsView):
form_class = SettingsForm
def get_initial(self):
initial = super(SettingsView, self).get_initial()
initial["extra_field"] = self.request.user.extra_field
return initial
def update_settings(self, form):
super(SettingsView, self).update_settings(form)
profile = self.request.user.userprofile
profile.extra_field = form_cleaned_data['extra_field']
profile.save()
class SignupView(account.views.SignupView):
form_class = SignupForm
def after_signup(self, form):
profile = self.created_user.userprofile
profile.extra_field = form_cleaned_data['extra_field']
profile.save()
super(SignupView, self).after_signup(form)
Once everything is in place, it should work nicely (hopefully).
I struggled with this topic for about a year off and on until I finally found a solution I was happy with, and I know exactly what you mean by "there is a lot out there, but it doesn't work". I had tried extending the User model in different ways, I had tried the UserProfile method, and some other 1-off solutions as well.
I finally figured out how to simply extend the AbstractUser class to create my custom user model which has been a great solution for many of my projects.
So, let me clarify one of your comments above, you really shouldn't be creating a link between 2 models, the generally accepted "best" solution is to have one model which is inherited from AbstractUser or AbstractBaseUser depending on your needs.
One tricky thing that got me was that "Extending the User Model" did not get me where I wanted and I needed to Substitute the User Model, which I'm sure you've seen/read multiple times, but possibly not absorbed it (at least I know I didn't).
Once you get the hang of it, there's really not that much code and it's not too complicated either.
# models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
'''
Here is your User class which is fully customizable and
based off of the AbstractUser from auth.models
'''
my_custom_field = models.CharField(max_length=20)
def my_custom_model_method(self):
# do stuff
return True
There are a couple things to look out for after this, some of which came up in django 1.7.
First of all, if you want the admin page to look like it did before, you have to use the UserAdmin
# admin.py
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
# Register your models here.
admin.site.register(get_user_model(), UserAdmin)
The other thing is that if you're wanting to import the User class in a models file, you have to import it from the settings and not with get_user_model(). If you run into this, it's easy to fix, so I just wanted to give you a heads up.
You can check out my seed project I use to start projects to get a full but simple project that uses a Custom User Model. The User stuff is in the main app.
From there all the Registration and Login stuff works the same way as with a normal Django User, so I won't go into detail on that topic. I hope this helps you as much as it has helped me!
I try to avoid to extend the user model as explained in the django docs.
I use this:
class UserExtension(models.Model):
user=models.OneToOneField(User, primary_key=True)
... your extra model fields come here
Docs of OneToOneField: https://docs.djangoproject.com/en/1.7/topics/db/examples/one_to_one/
I see these benefits:
the same pattern works for other models (e.g. Group)
If you have N apps, every app can extend the model on his own.
Creating the UserExtension should be possible without giving parameters. All fields must have sane defaults.
Then you can create a signal handler which creates UserExtension instances if a user gets created.
I prefer extend the User model. For example:
class UserProfile(User):
def __unicode__(self):
return self.last_name + self.first_name
department = models.CharField(max_length=100)
class SignupForm(forms.Form):
username = forms.CharField(max_length=30)
first_name = forms.CharField(max_length=30)
last_name = forms.CharField(max_length=30)
department = forms.CharField(label="Department", max_length=100)
To save the data
form = UserRegistrationForm(request.POST)
if form.is_valid():
client = UserProfile()
client.username = username
client.set_password(password)
client.first_name = first_name
client.department = department
client.save()
check how are you saving the data after validate the form

django - Joining LogEntry to actual models

so i'm using the admin LogEntry object/table to log events in my app. I have a view where i'd like to display each LogEntry.
It would be really great if i could join the LogEntry with the actual objects they represent (so i can display attributes of the object inline with the log entry)
In theory this should be easy as we have the model type and id from the LogEntry but i can't figure out how to join them using a queryset.
i thought i could just grab all the ids of the different objects and make another dictionary for each object type and then join them somehow (maybe zip the lists together?) but that seems dumb and not very djano-ish/pythonic.
does anybody have better suggestions?
** edit **
just want to clarify am not looking to use admin, but roll a custom view and template.
As I know Django uses contenttypes framework to perform logging in admin. So you should create generic relation inside your model and then to show inlines in admin use GenericTabularInline and GenericStackedInline. Please consult with the article.
from django.contrib import admin
from django.contrib.admin.models import LogEntry
from django.contrib.contenttypes.generic import GenericTabularInline
from django import forms
from some_app import models
from some_app.models import Item
class LogForm(forms.ModelForm):
class Meta:
model = LogEntry
class LogInline(GenericTabularInline):
ct_field = 'content_type'
ct_fk_field = 'object_id'
model = LogEntry
extra = 0
class ItemForm(forms.ModelForm):
class Meta:
model = Item
class ItemAdmin(admin.ModelAdmin):
form = ItemForm
inlines = [LogInline,]
admin.site.register(models.Item, ItemAdmin)
and you add to Item:
class Item(models.Model):
name = models.CharField(max_length=100)
logs = generic.GenericRelation(LogEntry)
this change won't create anything in your database, so there is no need to sync
Recent Django versions require to create a proxy for LogEntry:
from django.contrib import admin
from django.contrib.admin.models import LogEntry
from django.contrib.contenttypes.generic import GenericTabularInline
class LogEntryProxy(LogEntry):
content_object = GenericForeignKey('content_type', 'object_id')
class Meta:
proxy = True
class LogInline(GenericTabularInline):
model = LogEntry
extra = 0
class ItemAdmin(admin.ModelAdmin):
inlines = [LogInline,]
admin.site.register(models.Item, ItemAdmin)