Django how to use serializer with models - python

I am trying to use save() function in my model to auto populate the field via use of the api.. here is the code(models.py:
from .views import savee
class SaveRushh(models.Model):
keyword = models.ForeignKey(KW)
u1 = models.URLField()
u2 = models.URLField()
def save():
savee(keyword)
And the function itself:
from .serializer import RushSerializer
def savee(keyword):
nov = {'u1': 'https://en.wikipedia.org/wiki/Yellow', 'u2': 'https://en.wikipedia.org/wiki/White_color'}
nov['keyword'] = keyword
serializer = RushSerializer(data=nov)
if serializer.is_valid():
serializer.save()
And here is serializer code:
from rest_framework import serializers
from .models import SaveRushh
class RushSerializer(serializers.ModelSerializer):
# key = serializers.StringRelatedField(many=True)
class Meta:
model = SaveRushh
fields = ('keyword', 'u1', 'u2')
When I try to run it it gives this error:
File ".../serializer.py", line 2, in
from .models import SaveRushh ImportError: cannot import name SaveRushh
I think I understand that what I've done is something like circular import(SaveRushh already exists in models)
I just can't think of a good way to get around it.
To make it more clear, I want save() function inside my model to be ran every time model is created/updated, calling another function that uses rest api to populate model with data.

Related

Object has no attribute get in serializer

I created a serializer and an API endpoint so I can retrieve some data from a Django DB in my React app but getting this error message:
AttributeError: 'ProgrammingChallengesView' object has no attribute 'get'
Here is my models.py:
#creating programming challenges
class ProgrammingChallenges(models.Model):
challenge_id = models.AutoField(primary_key=True)
challenge_name = models.CharField(max_length=200)
challenge_description = models.TextField()
challenge_expectations = models.TextField()
my serializer:
from accounts.models import ProgrammingChallenges
...
class ProgrammingChallengesView(serializers.ModelSerializer):
class Meta:
model = ProgrammingChallenges
fields = '__all__'
and my urls.py:
path('api/programming_challenges/', ProgrammingChallengesView, name='programming_challenges'),
Thanks to the comments; I clearly didn't understand that a serializer only transforms my data to make it available through an API. I still had to create a view for my API's endpoint.
I opted to create a ReadOnlyModelView because I only want to GET data from this endpoint.
Here is what I wrote in my views:
class ProgrammingChallengesView(ReadOnlyModelViewSet):
serializer_class = ProgrammingChallengesSerializer
queryset = ProgrammingChallenges.objects.all()
#action(detail=False)
def get_list(self, request):
pass
and in my urls.py:
path('api/programming_challenges/', ProgrammingChallengesView.as_view({'get':'list'}), name='programming_challenges'),
I think you shouldn't hurry read the docs again. You are trying to use serializers as views.
Models - are representation of db tables as class.
Serializer serializes the data to json.
View accepts the reqeust from client and returns a Response.
Code shoudld be:
models.py
class ProgrammingChallenge(models.Model):
name = models.CharField(max_length=200)
description = models.TextField()
expectations = models.TextField()
Your model name should be ProgrammingChallenge(singular) not ProgrammingChallenges(plural).
You should't add prefix challenge before all field names. Because we already know that the fields are in a Model called ProgrammingChallenge. And it is easy to access them like ProgrammingChallenge.name than ProgrammingChallenge.challenge_name
You don't have to add field id manually. Django model automatically adds id field as primary_key
serializer.py
from accounts.models import ProgrammingChallenge
...
class ProgrammingChallengeSerializer(serializers.ModelSerializer):
class Meta:
model = ProgrammingChallenge
fields = '__all__'
No problem in serialize.
Now, main problem is you don't have any view. You definetly read docs. You can use APIView, generic views or viewset. In this example i'm going to use ViewSet that handles CRUD operations built in.
viewsets.py
from rest_framework.viewsets import ModelViewSet
from .models import ProgrammingChallenge
from .serializers import ProgrammingChallengSerializer
class ProgrammingChallengViewSet(ModelViewSet):
queryset = ProgrammingChallenge.objects.all()
serializer_class = ProgrammingChallengeSerializer
urls.py
from rest_framework.routers import SimpleRouter
from .viewsets import ProgrammingChallenge
router = SimpleRouter()
router.register('challengr', ProgrammingChallengeViewSet)
urlpatterns = router.urls
Another advantage of using viewset, it also generate all endpoint for it's CRUD methods automatically via routes.
It should help you to start your first project.
AGAIN, READ THE DOCS!

Django Attribute Error when using serializer

I would like to fetch the entire table. My model and serializer seems to be correct but I am getting the below error
Got AttributeError when attempting to get a value for field symbol on serializer CompanySerializer.
The serializer field might be named incorrectly and not match any attribute or key on the QuerySet instance.
Original exception text was: 'QuerySet' object has no attribute 'symbol'.
Below is my Model
models.py
from django.db import models
class Companies(models.Model):
symbol = models.CharField(max_length=100)
name = models.CharField(max_length=255)
isin = models.CharField(max_length=255)
serializers.py
from rest_framework import serializers
from .models import Companies
class CompanySerializer(serializers.ModelSerializer):
class Meta:
fields = ['symbol', 'name', 'isin',]
# fields = '__all__'
model = Companies
Below is my view
views.py
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Companies
from .serializers import CompanySerializer
from django.core.serializers import json
class companiesView(APIView):
def get(self, request):
companies = Companies.objects.filter(id=1)
serializer = CompanySerializer(companies)
# json_serializer = json.Serializer()
# json_serialized = json_serializer.serialize(companies)
response = Response()
response.data = {
'named' : serializer.data,
}
return response
I am not sure what is causing this issue. Thanks in Advance.
companies is a queryset, so what you have is a list of companies. If you want your serializer to work with a list of objects, just add many=True:
companies = Companies.objects.filter(id=1)
serializer = CompanySerializer(companies, many=True)
# ^^^ Add this
Another way is to just get the company with id 1 using get instead of filter. In this case you don't have to add many=True since the serializer is working on a single object:
companies = Companies.objects.get(id=1)
# ^^^ Use get instead of filter
serializer = CompanySerializer(companies)

Django dynamically generated serializer

Is there a way how to dynamically generate a Django rest framework serializers?
Considering this:
class BlogSerializer(serializers.ModelSerializer):
class Meta:
model = models.Blog
fields = get_all_model_fields(models.Blog)
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = models.Post
fields = get_all_model_fields(models.Post)
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = models.User
fields = get_all_model_fields(models.User)
I am wondering if something like following example could be possible:
from django.apps import apps
models = [model for model in apps.get_models()]
for model in models:
type(model.__name__+'Serializer',(serializers.ModelSerializer,),{
type("Meta",(),{
"model":model,
"fields": get_all_model_fields(model)
})
})
Or is there any other way how to generate DRF serializers?
Here's a function to build a ModelSerializer for a given Model (tested with Django 3.2, DRF 3.12.4 and Python 3.8):
from functools import lru_cache
from typing import Type
from django.db import models
from rest_framework import serializers
#lru_cache(maxsize=0)
def model_serializer(model: Type[models.Model]) -> Type[serializers.ModelSerializer]:
meta_class = types.new_class("Meta")
setattr(meta_class, "model", model)
setattr(meta_class, "fields", "__all__")
result = types.new_class(
model.__name__ + "Serializer", (serializers.ModelSerializer,), {}
)
setattr(result, "Meta", meta_class)
return result
If you are certain that you will call this function only once for each serializer, you can omit the #lru_cache to preserve some memory.
Example usage:
class MyModel(models.Model):
some = models.CharField(max_length=123)
other = models.IntegerField()
MyModelSerializer = model_serializer(MyModel)
my_serializer = MyModelSerializer({"some": "abc", "other": 1234})
my_serializer.is_valid(True)
To add the serializers for all your models to the current module's namespace:
from django.apps import apps
for model in apps.get_models():
serializer_type = model_serializer(model)
globals()[serializer_type.__name__] = serializer_type
Your approach will work - but for Django to find about these serializers you have to assign them to the module namespace.
In your code, you just call type - the serializer class is created and "thrown away" immediately. Even if the base Django class serializers.ModelSerializer keep a reference to it in a registry, you would not be able to import your serializers and make use of them.
All you have to do is to add them to the dictionary returned by globals(). Likewise, the namespace you create for the class also has to be a dictionary -since you are calling "type" but not actually assigning its name as "Meta", you create a set, not a dictionary, and your call to type will fail as it was expecting a dictionary.
So, I did not check if the code actually will work, but the idea, based on yours is:
from django.apps import apps
models = [model for model in apps.get_models()]
for model in models:
name = f"{model.__name__}Serializer"
globals()[name] = type(name,(serializers.ModelSerializer,),{
"Meta": type("Meta",(),{
"model":model,
"fields": get_all_model_fields(model)
})
})
del models, model, name

AttributeError: (Class) object has no attribute '__name__' Creating ModelForms [Django & Python2.7]

This is my first time using Django and I am completely stuck at how to use ModelForms in my project. I have been able to follow the online tutorials this far but without ModelForms(to add data into a Postgresql database), I can't proceed onward. I am trying to simply make a form page that lets the users add a few inputs (2 datefields and 1 textfield) and by submitting that form, the data will be added to the database.
The error I have been getting is:
AttributeError: 'Hyuga_Requests' object has no attribute 'name' [where Hyuga_Request is a class set in the models.py]
models.py
from __future__ import unicode_literals
from django.db import models
from django.forms import ModelForm
class Hyuga_Requests(models.Model):
name = models.CharField(max_length=50)
s_date = models.DateField(auto_now=True)
e_date = models.DateField(auto_now=True)
reason = models.TextField(max_length=500)
def __unicode__(self):
return self.name
views.py
from django.shortcuts import render
from django import forms
from .forms import Hyuga_RequestForm
def create_req(request):
form = Hyuga_RequestForm()
context = {"form":form,}
return render(request,"request_form/requestform.html", context)
forms.py
from django import forms
from .models import Hyuga_Requests
from django.forms import ModelForm
class Hyuga_RequestForm(forms.ModelForm):
class Meta:
model = Hyuga_Requests()
fields = ['name','s_date','e_date','reason']
Please help this noobie...
Don't instantiate the model in the class Meta inside the Hyuga_RequestForm class.
model = Hyuga_Requests() should be model = Hyuga_Requests
model = Hyuga_Requests() -> model = Hyuga_Requests
The error come because you are calling the model on the form.
from django import forms
from .models import Hyuga_Requests
from django.forms import ModelForm
class Hyuga_RequestForm(forms.ModelForm):
class Meta:
model = Hyuga_Requests
fields = ['name','s_date','e_date','reason']
Note: i suggest to you use on the any class you define on python not use "_", you can check more about PEP8 and code styles here: https://www.python.org/dev/peps/pep-0008/

How to add custom fields to InlineFormsets?

I'm trying to add custom fields to an InlineFormset using the following code, but the fields won't show up in the Django Admin. Is the InlineFormset too locked down to allow this? My print "ding" test fires as expected, I can print out the form.fields and see them all there, but the actual fields are never rendered in the admin.
admin.py
from django.contrib import admin
import models
from django.forms.models import BaseInlineFormSet
from django import forms
from forms import ProgressForm
from django.template.defaultfilters import slugify
class ProgressInlineFormset(BaseInlineFormSet):
def add_fields(self, form, index):
print "ding"
super(ProgressInlineFormset, self).add_fields(form, index)
for criterion in models.Criterion.objects.all():
form.fields[slugify(criterion.name)] = forms.IntegerField(label=criterion.name)
class ProgressInline(admin.TabularInline):
model = models.Progress
extra = 8
formset = ProgressInlineFormset
class ReportAdmin(admin.ModelAdmin):
list_display = ("name", "pdf_column",)
search_fields = ["name",]
inlines = (ProgressInline,)
admin.site.register(models.Report, ReportAdmin)
I did it another way:
forms.py:
from django import forms
class ItemAddForm(forms.ModelForm):
my_new_field = forms.IntegerField(initial=1, label='quantity')
class Meta:
model = Item
admin.py:
from django.contrib import admin
from forms import *
class ItemAddInline(admin.TabularInline):
form = ItemAddForm
fields = (..., 'my_new_field')
This works so far, I only need to override somehow the save method to handle this new field. See this: http://docs.djangoproject.com/en/dev/ref/contrib/admin/#form . It says that by default Inlines use BaseModelForm, which is send to formset_factory. It doesn't work for me, tried to subclass BaseModelForm with errors (no attribute '_meta'). So I use ModelForm instead.
You can do it by another way (Dynamic forms):
admin.py
class ProgressInline(admin.TabularInline):
model = models.Progress
extra = 8
def get_formset(self, request, obj=None, **kwargs):
extra_fields = {'my_field': forms.CharField()}
kwargs['form'] = type('ProgressForm', (forms.ModelForm,), extra_fields)
return super(ProgressInline, self).get_formset(request, obj, **kwargs)
model = models.Progress
In the admin there will be only the fields defined in this Progress model. You have no fields/fieldsets option overwriting it.
If you want to add the new ones, there are two options:
In the model definition, add those new additional fields (make them optional!)
In the admin model (admin.TabularInline), add something something like:
fields = ('newfield1', 'newfield2', 'newfield3')
Take a look at fields, fieldsets.

Categories