I'm trying to make an app with Django using MongoDB as my database engine, so I'm using Djongo as ORM.
In my models, I have defined a Client class, which is intended to contain an array of Profiles (authorized people to login in name of client).
In the Djongo Documentation, it says one can use the ArrayField class in models in order to store an array to the database. The point is that I followed the example in the documentation (even I tried to copy without changing anything) and it isn't working. When running the view, I always get this error:
"Value: Profile object (None) must be an instance of <class 'list'>"
I have the following models.py:
from django import forms
from djongo import models
class Profile(models.Model):
_id=models.ObjectIdField()
fname = models.CharField(max_length=25)
lname = models.CharField(max_length=50)
email = models.EmailField()
password = models.CharField(max_length=16)
class Meta:
abstract=True
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields =(
'fname', 'lname', 'email', 'password',
)
class Client(models.Model): #informacion del cliente
_id = models.ObjectIdField()
name = models.CharField(max_length=256)
authorized = models.ArrayField(
model_container=Profile,
model_form_class=ProfileForm
)
objects = models.DjongoManager()
class ClientForm(forms.ModelForm):
class Meta:
model = Client
fields = (
'name', 'authorized'
)
I have a form that uses ClientForm, and the view renders properly. However, when submitting the form I get the error I said at the beginning. I'm searched the whole internet and didn't get any idea of what is causing this problem.
I have bumped into a similar error a couple of days ago, I was trying to store an object (in your case it's Profile) directly to the ArrayField, I was trying to do something like:
client = Client.object.first()
client.authorized = {
'fname': form.validated_data['first name'],
'last': form.validated_data['last name'],
'email': form.validated_data['email'],
'password': form.validated_data['Password'],
}
client.save()
the right way to do it is:
client = Client.object.first()
client.authorized = [{
'fname': form.validated_data['first name'],
'last': form.validated_data['last name'],
'email': form.validated_data['email'],
'password': form.validated_data['Password'],
}]
client.save()
you'll need to store it as a list object by wrapping it around []
that's how I was able to solve it in my case, I think yours is similar, try the above solution, and if it doesn't work, maybe you should share more details like your views.py
Related
I am using Django 3.2 and Django Rest Framework 3.14.
I have Users that should be able to follow other Users, like on a social networking site. When serializing a full User, I would like to get a list of those following Users, as well as additional data, such as the follow datetime. However, I would like the response to stay "flat", like this:
{
"username":"admin",
"followers":[
{
"username":"testuser",
--additional user fields--
"follow_date":"2023-02-08 01:00:02"
},
--additional followers--
],
--additional user fields--
}
I can only seem to go "through" my join model using an extra serializer, and end up with this:
{
"username":"admin",
"followers":[
{
"user":{
"username":"testuser",
--additional user fields--
},
"follow_date":"2023-02-08 01:00:02"
},
--additional followers--
],
--additional user fields--
}
Note how there is an additional user key
"user":{
"username":"testuser",
--additional user fields--
},
I don't want that there!
My model looks something like this:
class Follower(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.RESTRICT, related_name="followers")
follower = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.RESTRICT, related_name="following")
follow_date = models.DateTimeField(auto_now_add=True)
My serializers look like this (extra fields trimmed for brevity):
class UserSnippetSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username']
class FollowerSerializer(serializers.ModelSerializer):
user = UserSnippetSerializer(source='follower')
class Meta:
model = Follower
fields = ['user', 'follow_date']
class FullUserSerializer(serializers.ModelSerializer):
followers = FollowerSerializer(source='user.followers', many=True)
following = FollowerSerializer(source='user.following', many=True)
class Meta:
model = Profile
fields = ['username', 'followers', 'following']
Given this, I understand why it's doing what it is. But I can't seem to find any way to skip the extra user key, while still including bot the User information AND the follow_date.
I've tried playing around with proxy models on User, but that didn't seem to help. I suspect that this is no configuration thing, though I would like it to be, and that I need to override some internal function. But I can't seem to locate what that would be.
What's the best (and hopefully easiest!) way to accomplish this?
One approach is to use a serializer method field on FollowerSerializer like this:
class FollowerSerializer(serializers.ModelSerializer):
username = serializers.SerializerMethodField()
class Meta:
model = Follower
fields = ['username', 'follow_date']
def get_username(self, obj):
return obj.follower.username
or if you have lots of fields to display from user, you can also override to_representation like this:
class FollowerSerializer(serializers.ModelSerializer):
user = UserSnippetSerializer(source='follower')
class Meta:
model = Follower
fields = ['user', 'follow_date']
def to_representation(self, instance):
data = super().to_representation(instance)
user_data = data.pop("user")
# flatten the follower data with the user data
return {
**data,
**user_data,
}
Note that this will also affect FullUserSerializer.following
I am working on a basic rest api with django rest framework. And my database is on MySQL. For one of the functions, when I try to POST, it gives me an error because there is a row with the same username already. I have set the model to have all three of its fields to be unique_together. Please help me understand where i am going wrong, heres snippets of what i have currently:
models.py:
class VolunteerHours(models.Model):
username = models.OneToOneField(Volunteer, models.DO_NOTHING, db_column='Username', primary_key=True)
date = models.DateField(db_column='Date')
hours = models.IntegerField(db_column='Hours')
class Meta:
managed = False
db_table = 'volunteer_hours'
unique_together = (('username', 'date', 'hours'),)
urls.py:
urlpatterns = [
path('timesheet/', views.TimesheetAPIView.as_view()),]
views.py:
from . import models
from . import serializers
from rest_framework import generics
from rest_framework import mixins
class TimesheetAPIView(generics.GenericAPIView, mixins.CreateModelMixin):
serializer_class = serializers.VolunteerTimesheetSerializer
def post(self, request):
return self.create(request)
serializers.py:
class VolunteerTimesheetSerializer(serializers.ModelSerializer):
class Meta:
model = models.VolunteerHours
fields = '__all__'
The error im getting is:
"username": [
"volunteer hours with this username already exists."
]
What I want to happen is i can add as many rows with the same username as long as the date and hours are unique. Which are what is submit in my POST requests, but it says username already exists even if the date and hours are unique.
Please help.
Thank you!
You’ve created a OneToOne relationship with VolunteerHours and Volunteers, meaning only one of each can exist in a relationship. Like #DEEPAK KUMAR says, you’ll want to remove primary_key=True, but you’ll also want to change the relationship to be OneToMany via the ForeignKey field.
Hello Stackoverflowers!
I've searched for a solution for this for a while, but have not been able to find anything addressing this usecase specifically.
Say we have the following models:
class Machine(models.model):
machine_name = models.CharField(max_length=30)
machine_data_template = models.JSONField()
class Event(models.model):
machine_name = models.ForeignKey(Machine, on_delete=models.CASCADE
machine_data = models.JSONField()
We have a machine for which there can be multiple events, the details of which are stored as JSON in a JSONField. The JSONField in event should be validated using the json-schema defined in Machine.machine_data_template.
I've looked at various Python/Django implementations of json-schemas, however I have not been able to find a way where we can define custom validation on a per-object basis. For example, solutions such as the one presented by Corwin Cole here defines the validation logic directly in the model, which would mean each instance of event would be subject to the same validation logic regardless of the machine:
MY_JSON_FIELD_SCHEMA = {
'schema': 'http://json-schema.org/draft-07/schema#',
'type': 'object',
'properties': {
'my_key': {
'type': 'string'
}
},
'required': ['my_key']
}
class MyModel(models.Model):
my_json_field = JSONField(
default=dict,
validators=[JSONSchemaValidator(limit_value=MY_JSON_FIELD_SCHEMA)]
)
Does anyone have any suggestion how I can achieve the desired "fully polymorphic" behavior?
BR - K
You can override the Model.clean() method and add custom validation there. You have access to the Event and it's related Machine so can access the schema. Add an example using jsonschema similar to the linked answer
import jsonschema
class Machine(models.Model):
machine_name = models.CharField(max_length=30)
machine_data_template = models.JSONField()
class Event(models.Model):
machine_name = models.ForeignKey(Machine, on_delete=models.CASCADE)
machine_data = models.JSONField()
def clean(self):
try:
jsonschema.validate(self.machine_data, self.machine_name.machine_data_template)
except jsonschema.exceptions.ValidationError as e:
raise ValidationError({'machine_data': str(e)})
I have the following setup:
class Ride(models.Model):
...
riders = models.ManyToManyField(User, through='RideRiders', null=True, blank=True)
class Meta:
db_table = 'rides'
app_label = 'rides'
class RideRiders(models.Model):
user = models.ForeignKey(User)
ride = models.ForeignKey(Ride)
# Other information
...
class Meta:
db_table = 'rides_riders'
app_label = 'rides'
So, for example, i can get all the rides for a user using the following (is this actually the best way?):
Ride.objects.filter(riders__id=user_id)
I want to be able to get all the users for a particular ride but can't seem to figure out how. This RideRiders model seems to be the sticking point as i can't seem to pass beyond that to the user. For example:
Ride.objects.get(id=ride_id).riders.all()
...gives a list of RideRider objects (i think).
Any suggestions?
EDIT:
To add more context, i'm attempting to fetch the riders for a particular ride as an API call using the Django Rest Framework. This is how i'm using it:
class RideRiders(generics.ListAPIView):
model = User
serializer_class = RiderSerializer
def get_queryset(self):
return Ride.objects.get(id=self.kwargs.get('id')).riders.all()
But this is currently giving me the error which is why i feel it's returning RideRider objects rather than Rider objects.
FieldError: Cannot resolve keyword u'ride' into field. Choices are: date_joined, email, first_name, groups, id, is_active, is_staff, is_superuser, last_login, last_name, logentry, password, user_permissions, username
EDIT 2:
So Ride.objects.get(id=self.kwargs.get('id')).riders.all() is throwing the above error (nothing to do with serializers as thought). If i run Ride.objects.get(id=self.kwargs.get('id')).rideriders_set.all() then it returns a queryset (and testing gives the expected number of records for any particular ride). So, this leaves the original question of how do i return a list of users (rather than RideRider objects)?
The culprit turned out to be app_label = 'rides' in the model classes was incorrect after some refactoring of the overall application. This was clearly stopping Django from making the proper lookups across models as they weren't being registered properly.
I'm trying relate two resources (models) in an API using Tastypie but I'm getting an error.
I've followed the django tutorial and used:
models.py
from django.db import models
class Poll(models.Model):
question = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
class Choice(models.Model):
poll = models.ForeignKey(Poll)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
I tried to create a link between the Poll and Choice based on this stackoverflow answer and wrote the following code:
api.py
class ChoiceResource(ModelResource):
poll = fields.ToOneField('contact.api.PollResource', attribute='poll', related_name='choice')
class Meta:
queryset = Choice.objects.all()
resource_name = 'choice'
class PollResource(ModelResource):
choice = fields.ToOneField(ChoiceResource, 'choice', related_name='poll', full=True)
class Meta:
queryset = Poll.objects.all()
resource_name = 'poll'
When I go to: 127.0.0.1:8088/contact/api/v1/choice/?format=json
Everything works as it should. For example one of my choices links to the right poll:
{
"choice_text": "Nothing",
"id": 1,
"poll": "/contact/api/v1/poll/1/",
"resource_uri": "/contact/api/v1/choice/1/",
"votes": 6
}
When I go to: 127.0.0.1:8088/contact/api/v1/poll/?format=json
I get:
{
"error": "The model '<Poll: What's up?>' has an empty attribute 'choice' and doesn't allow a null value."
}
Do I need to use the fields.ToManyField instead or do I need to change my original model?
Tastypie recommends against creating reverse relationships (what you're trying to do here the relationship is Choice -> Poll and you want Poll -> Choice), but if you still wanted to, you can.
Excerpt from the Tastypie docs:
Unlike Django’s ORM, Tastypie does not automatically create reverse
relations. This is because there is substantial technical complexity
involved, as well as perhaps unintentionally exposing related data in
an incorrect way to the end user of the API.
However, it is still possible to create reverse relations. Instead of
handing the ToOneField or ToManyField a class, pass them a string that
represents the full path to the desired class. Implementing a reverse
relationship looks like so:
# myapp/api/resources.py
from tastypie import fields
from tastypie.resources import ModelResource
from myapp.models import Note, Comment
class NoteResource(ModelResource):
comments = fields.ToManyField('myapp.api.resources.CommentResource', 'comments')
class Meta:
queryset = Note.objects.all()
class CommentResource(ModelResource):
note = fields.ToOneField(NoteResource, 'notes')
class Meta:
queryset = Comment.objects.all()