In django, I have two models - User and UserProfile. There may exist zero or one profiles for a particular user. I'm trying to include the information from the UserProfile model directly on the UserResource.
I would like to use the profile ToManyField, if it exists, to access the contents of the associated UserProfile model. I've tried a variety of things in dehydrate, including self.profile.get_related_resource(self) and UserProfile.objects.get(id=...), but I can't seem to find my way from the profile field to the model object. Can anybody help me out?
I'm still new to Python, Django, and Tastypie, so hopefully if I'm doing anything awful somebody will be kind enough to point it out.
The goal is to have JSON that looks like this:
{
resourceUri: /v1/users/1
date_of_birth: Jan 1, 1980
... etc
}
where date_of_birth is a property of the UserProfileResource. I don't want all of the fields from UserProfileResource, and I don't want the UserProfile to be a nested object in the response - I want some fields from UserProfileResource to be top-level fields in the response, so that they look like part of the User resource.
class UserResource(ModelResource):
profile = fields.ToOneField('foo.api.UserProfileResource', 'user', null=True)
class Meta:
queryset = User.objects.all()
resource_name = 'users'
allowed_methods = ['get']
#etc...
class UserProfileResource(ModelResource):
date_of_birth = ...
#etc
I assume you're using Django 1.4 and AUTH_PROFILE_MODULE?
Since the User:UserProfile relationship is 1:1 I would opt for the ToOneField instead. This will serialize as a URI pointer to your UserProfileResource (if one exists.) If you'd like the UserProfileResource field data inline with your UserResource, you can specify full=True in the ToOneField definition. With this method you should not need to override dehydrate.
Also, ensure that the second argument in the ToOneField definition is the User attribute which points to your UserProfile Django model. For example if you have OneToOneField(User, related_name='profile') in your Django model then this attribute should be profile.
class UserResource(ModelResource):
profile = fields.ToOneField('foo.api.UserProfileResource', 'profile',
full=True, null=True)
class Meta:
queryset = User.objects.all()
resource_name = 'users'
allowed_methods = ['get']
If what you're after are specific fields from the UserProfile instance mixed in with your User, you should be able to do something like this:
class UserResource(ModelResource):
date_of_birth = fields.DateField('profile__date_of_birth', null=True)
class Meta:
queryset = User.objects.all()
resource_name = 'users'
allowed_methods = ['get']
fields = ['userfields', 'gohere', 'date_of_birth']
Related
First off I want to say this question is similar to this one which references this one. I have the exact same question as the second link except a notable difference. I'm trying to extend a class generated by NestJS which defines a property.
I'm using NestJs with the Schema first approach found here. I'm also generating a classes file based on my GraphQL Schema.
Here is the Schema:
type Location {
name: String!
owner: User!
}
Which generates the class:
export class Location {
name: string;
owner: User;
}
Now, I want to extend this class so I don't have to repeat the data (there are a lot more fields not shown). I also I want to add fields that live on a document but are not in the schema (_id in this example). Here is my LocationDocument and my schema.
export interface LocationDocument extends Location, Document {
_id: Types.ObjectId
}
export const LocationSchema: Schema = new Schema(
{
name: {
type: String,
required: true,
},
owner: {
type: Types.ObjectId,
ref: 'User',
}
);
Now here is my issue. The generated Location class from the GraphQL schema defines the owner property as a User type. But in reality it's a just a mongodb id until it is populated by Mongoose. So it could be a Types.ObjectId or a User on a UserDocument. So I attempted to define it as:
export interface LocationDocument extends Location, Document {
_id: Types.ObjectId
owner: User | Types.ObjectId;
}
But this throws an error in the compiler that LocationDocument incorrectly extends Location. This makes sense. Is there any way to extend the User Class but say that owner property can be a User Type (once populated by Mongoose) or a mongo object ID (as is stored in the database).
I decided that having a property that can be both types, while easy with Mongoose and JS, isn't the typed way. In my schema I have an owner which is a User type. In my database and the document which extends it, I have an OwnerId. So to people accessing the API, they don't care about the ownerId for the relationship. But in my resolver, I use the Id. One is a Mongo ID type, the other is a User type.
I use graphene with graphene-mongo. My graphql schema has a type similar to this:
type Report {
id:ID!
name:String!
}
My graphene class for this type is
class Product(MongoengineObjectType):
class Meta:
model = MongoProduct
and the mongoengine class is
class MongoProduct(mng.DynamicDocument):
name = mng.fields.StringField(required=True)
How can I make the field id required? GraphiQL shows an exclamation mark next to name, but not next to id.
class MongoProduct(mng.DynamicDocument):
id = ObjectIdField(primary_key=True, required=True) # Optional: Add default=bson.ObjectId
name = mng.fields.StringField(required=True)
id can also be a IntField or a StringField but I'd recommend to stick to an ObjectId
I'm wondering is there anyway to optimize an api when using foreign key and ManytoMany field, for example :
Serializer :
class SerializerA(serializers.ModelSerializer):
class Meta:
model = Model_A
fields = ('id', 'official_name', 'gender')
depth = 1
class SerializerB(serializers.ModelSerializer):
user = SerializerA(many=True)
class Meta:
model = Model_B
fields = ('id', 'project_name','project_type', 'project_start_date', 'user')
depth = 1
API :
class ReportAPI(APIView):
def get(self, request):
all_projects = Model_B.objects.all()
project_serializer = SerializerB(all_projects, many=True)
return Response(project_serializer.data)
now with this, if i go to the API url, and debug this page, it''l show that I'm querying 78 times from the SQL query. But if I remove one field from manytoMany seriealizer field which the 'gender', the page now will only query from the database 21 times, so my question is again, how can I optimize this?
You can use select_related (for ForeignKey) and/or prefetch_related (For ManyToMany or ManyToOne) so that it will not hit the database for each Model_B object.
If user is a FK in Model_B then you can do:
class ReportAPI(APIView):
def get(self, request):
all_projects = Model_B.objects.all().select_related('user')
project_serializer = SerializerB(all_projects, many=True)
return Response(project_serializer.data)
If the user model has other FK that you need in it's serializer, then you can also do select_related('user', 'user__other_field').
I can't seem to understand where I am going wrong currently when I attempt to update my User domain model that has a hasOne association to a Profile object.
My domain models are as follows:
class User {
static hasOne = [profile: Profile]
static fetchMode = [profile: 'eager']
ObjectId id
String username
}
class Profile {
static belongsTo = [user: User]
ObjectId id
String familyName
String givenName
}
I am able to persist a User with a profile originally but when attempting to update the User object I get validation errors.
Validation error occurred during call to save():
- Field error in object 'co.suitable.User' on field 'profile.familyName': rejected value [null]
- Field error in object 'co.suitable.User' on field 'profile.givenName': rejected value [null]
I am able to print out the user.profile ID and also the user.profile.familyName before saving the object. Like the following:
println(user.profile.familyName)
println(user.profile.id.toString())
user.save(flush: true, failOnError: true)
But I still get the validation errors before saving, i'd imagine that the println(user.profile.familyName) call is fetching the profile object if it already hasn't been loaded which I thought setting the fetchMode would have handled.
The object is able to successfully persist and save when I do:
user.profile = Profile.findById(user.profile.id)
println(user.profile.id.toString())
user.save(flush: true, failOnError: true)
I could wrap that in a service but I was hoping for a solution that would be handled by Grails if possible. Any advice or thoughts is much appreciated.
You should not apply the logic for the SQL DB to Mongo 1 to 1. Mongo and other document-oriented DBs are not originally intended to store the joins between collections. There are some workarounds, like db-refs, but they are to be used with caution.
For your case - with hasOne - I would suggest using mongo's subdocuments (mirrored as GORM's embedded objects) instead of referencing:
class User {
ObjectId id
String username
Profile profile
static embedded = [ 'profile' ]
}
class Profile {
String familyName
String givenName
}
thus you use the mongo in accordance to it's original puprose. Also querying is simpler and faster.
I need to store a record in First Model. It has around 10 fields. Now, I need to apply required rule for one field which i'm storing in SECOND model and I'm getting these values for this field from Third model.(it is a drop down field)
how can i make this field mandatory in yii??
can any one help please..
Many Thanks,
You can add additional attributes and rules to a model. You don't have to only use attributes that directly relate to fields in your database table. Let's look at a basic example. You have a user (User) table which has the following fields:
id
email
password
And you have another table which stores user profile (UserProfile) information which has the structure:
id
user_id
country
When the user creates their account, you would have a form that captures their information. Your User model would have rules like:
array('email, password', 'required'),
array('email', 'unique', 'message'=>'Email already in use'),
...
You can add an attribute for country to your User model like so:
class User extends CActiveRecord {
public $country;
...
And then in your rules you can add the new attribute:
array('email, password, country', 'required'),
array('email', 'unique', 'message'=>'Email already in use'),
...
The country attribute will now be a part of your User model. You can now add that to your form:
<?php echo $form->dropDownList($model,'country',CHtml::listData(Country::model()->findAll(),'id','country_name'),array('empty'=>'-- select a country --')); ?>
Now on your form submit, the $model->validate() method will validate the country field. You can save this manually to your second model (UserProfile), something like:
if(isset($_POST['User'])){
if ($model->validate()){
$user_profile = new UserProfile;
$user_profile->user_id = $model->id;
$user_profile->country = $model->country;
$user_profile->save();
...
Hopefully that answers your question.