I want delete serializer's content to show.I am using Django Rest Framework.I am making a system return Json of serializers.Furthermore,I did not want to show user_id data. I wrote in views.py
class InfoViews(viewsets.ModelViewSet):
queryset = Info.objects.all()
serializer_class = InfoSerializer
lookup_field = 'id'
def update(self,request, *args, **kwargs):
obj = UserInfo.objects.get(pk=kwargs['id'])
data = request.data
info_serializers = InfoSerializer(obj, data = data)
if info_serializers.is_valid(raise_exception=True):
info_serializers.save()
del info_serializers.data['user_id']
return JsonResponse(info_serializers.data)
Now all son data is shown.What is wrong in my code?How should I fix this?
You can specify in the serializers which fields you want to include or exclude in the response:
http://www.django-rest-framework.org/api-guide/serializers/#specifying-which-fields-to-include
look for : Specifying which fields to include
hope this helpes
First Clone the object into a new variable and then pop the key.
if info_serializers.is_valid(raise_exception=True):
info_serializers.save()
import copy
new = copy.deepcopy(info_serializers.data)
new.pop('user_id', None)
return JsonResponse(new)
Related
I'm new to Django and I'm having a hard time understanding forms when the data to choose from are not taken from the database nor user input that they're generated on the go.
I currently have a template with a single ChoiceField. The data inside this field aren't fixed and they're calculated on the go once the page is requested. To calculate it I need the username of the User who is logged in. Basically, the calculation returns a list of lists in the form of ((title, id),(title,id),(title,id)), etc. that I need to put into the ChoiceField to make the User choose from one of the options.
Now, I'm not understanding how to pass the calculated list of lists to the form. I've tried to add the calculations inside the form as below but it is clearly the wrong way.
The main issue is that, to calculate my list of lists, I need the request value, and I don't know how to access it from the form.
Another idea was to add the generate_selection function inside the init but then I don't know how to pass main_playlist to being able to add it to ChoiceField
Below my not working forms.py
forms.py
class ChoosePlaylistForm(forms.Form):
playlists = forms.ChoiceField(choices=HERE_SHOULD_GO_main_playlist)
def generate_selection(self):
sp_auth, cache_handler = spotify_oauth2(self.request)
spotify = spotipy.Spotify(oauth_manager=sp_auth)
user_playlists = spotify.current_user_playlists(limit=10)
main_playlist = []
for playlists in user_playlists["items"]:
playlists_list = []
playlists_list.append(playlists['name'])
playlists_list.append(playlists['id'])
main_playlist.append(playlists_list)
return main_playlist
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(ChoosePlaylistForm, self).__init__(*args, **kwargs)
class Meta:
model = User
fields = ('playlists',)
The views should be something like below so I'm able to pass the request
views.py
form = ChoosePlaylistForm(request=request)
Maybe overriding the field choices in the form constructor would work:
class ChoosePlaylistForm(forms.Form):
playlists = forms.ChoiceField(choices=())
class Meta:
model = User
fields = ('playlists',)
def __init__(self, *args, request=None, **kwargs):
super(ChoosePlaylistForm, self).__init__(*args, **kwargs)
self.request = request
self.fields['playlists'].choices = self.generate_selection()
def generate_selection(self):
sp_auth, cache_handler = spotify_oauth2(self.request)
spotify = spotipy.Spotify(oauth_manager=sp_auth)
user_playlists = spotify.current_user_playlists(limit=10)
choices = []
for playlist in user_playlists["items"]:
playlist_choice = (playlist["name"], playlist["id"])
choices.append(playlist_choice)
return choices
Currently I'm trying to create an expected json to use in my test:
#pytest.mark.django_db(databases=['default'])
def test_retrieve_boards(api_client):
board = baker.make(Board)
objs = BoardSerializerRetrieve(board)
print(objs.data)
url = f'{boards_endpoint}{board.id}/'
response = api_client().get(url)
assert response.status_code == 200
But i'm receiving the following error:
AttributeError: Got AttributeError when attempting to get a value for field `cards_ids` on serializer `BoardSerializerRetrieve`.
E The serializer field might be named incorrectly and not match any attribute or key on the `Board` instance.
E Original exception text was: 'Board' object has no attribute 'cards_ids'
Currently cards_idsare added on my viewSet on get_queryset method:
def get_queryset(self):
#TODO: last update by.
#TODO: public collections.
"""Get the proper queryset for an action.
Returns:
A queryset object according to the request type.
"""
if "pk" in self.kwargs:
board_uuid = self.kwargs["pk"]
qs = (
self.queryset
.filter(id=board_uuid)
.annotate(cards_ids=ArrayAgg("card__card_key"))
)
return qs
return self.queryset
and this is my serializer:
class BoardSerializerRetrieve(serializers.ModelSerializer):
"""Serializer used when retrieve a board
When retrieve a board we need to show some informations like last version of this board
and the cards ids that are related to this boards, this serializer will show these informations.
"""
last_version = serializers.SerializerMethodField()
cards_ids = serializers.ListField(child=serializers.IntegerField())
def get_last_version(self, instance):
last_version = instance.history.first().prev_record
return HistoricalRecordSerializer(last_version).data
class Meta:
model = Board
fields = '__all__'
what is the best way to solve it? I was thinking in create a get_cards_ids method on serializer and remove annotate, but I don't know how to do it and justing googling it now. rlly don't know if this is the correct way to do it.
Test the view, not the serializer, i.e. remove BoardSerializerRetrieve(board) from your test code.
cards_ids is annotated on ViewSet level. The annotated queryset is then passed to serializer.
#pytest.mark.django_db(databases=['default'])
def test_retrieve_boards(api_client):
board = baker.make(Board)
url = f'{boards_endpoint}{board.id}/'
response = api_client().get(url)
assert response.status_code == 200
Also, instead of building the URL manually with url = f'{boards_endpoint}{board.id}/', consider using reverse, e.g. url = reverse("path-name", kwargs={"pk": board.id}).
There is a possibility to solve my problem, please.
I have 3 molds, of which 2 are related.
I am using create on ModelViewSet, which sent the data but I am getting the following error.
(list indexes must be integer or chunked, not str)
Of course I am trying to create a list of objects, but.
¿How would you develop the syntax? With the use of For?
ModelViewSet
class CreateApiModelViewSet(viewsets.ModelViewSet):
serializer_class = EDFSerializer
def get_queryset(self):
dano = models.Dano.objects.all()
return dano
def create(self, request, *args, **kwargs):
data = request.data
new_evento = models.Evento.objects.create(
tabla=data["evento"]["tabla"],
usuario=models.Usuario.objects.filter(user_id=data["evento"]["usuario"]).first(),
patio=models.Patio.objects.filter(id=data["evento"]["patio"]).first()
)
new_evento.save()
# New Dano
new_dano = models.Dano.objects.create(
evento=new_evento,
observacion=data["observacion"])
new_dano.save()
# Model FotoDano With Error.. :(
foto = []
for fotos in foto:
for f in data["fotodanodetail"]["foto"][0]:
foto_obj = models.FotoDano.objects.get(
foto=data["fotodanodetail"]["foto"],
dano=new_dano)
new_foto_dano.foto.add(foto_obj)
# Comment
# new_foto_dano = models.FotoDano.objects.create(
# #id=data["fotodanodetail"]["id"],
# #foto=data["fotodanodetail"]["foto"],
# dano=new_dano)
# new_foto_dano.save()
serializer = EDFSerializer(new_evento)
return Response(serializer.data)
Serializer
# Import Base64
from drf_extra_fields.fields import Base64ImageField
class EDFSerializer(serializers.ModelSerializer):
fotodanodetail = Base64ImageField(required=False)
evento = CrearEventoSerializer()
fotodanodetail = FotoDanoFiltroSerializer(many=True)
class Meta:
model = models.Dano
fields = ('evento','observacion','fotodanodetail')
Postman
Based on DRF when you have writeable nested serializer best way is override .create method of your serializer rather than viewset
for more information checkout drf docs here
All data in request.data have str type you must cast like int(data["evento"]["usuario"])
I need to pass a parameter in the URL to the Django Admin add view, so that when I type the URL: http://localhost:8000/admin/myapp/car/add?brand_id=1, the add_view reads the brand_id parameter. I want to use this so that I can set a default value for the brand attribute of Car.
def get_form(self, request, obj=None, **kwargs):
form = super(CarAdmin, self).get_form(request, obj, **kwargs)
form.base_fields['brand'].initial = <<GET['brand_id']>>
return form
Use case: I want this because in my BrandsAdmin, I have added a "+" button for each brand, that should add a Car with that Brand as FK.
I've tried getting it from request in get_form, but when that code is executed, Django has already changed my URL as I guess it doesn't understand it as a "legal" parameter.
Thanks a lot!
Using django 3 i did it overriding get_form in CarAdmin.py like you did, and its working for me, maybe the trick is in Brand.py:
Brand.py
readonly_fields = ('get_add_link',)
def get_add_link(self, obj):
url = f"{reverse('admin:myapp_car_add')}?brand={self.id}"
return format_html(f"<a href='{url}'><i class='fas fa-plus'></i></a>")
CarAdmin.py
def get_form(self, request, obj=None, **kwargs):
form = super(CarAdmin, self).get_form(request, obj, **kwargs)
brand_id = request.GET.get('brand')
if brand_id and len(brand_id) > 0:
form.base_fields['brand'].initial = brand_id
You can find a full explanation here, but shortly:
You can access query parameters directly from the view, the request object has a method. I suppose it is a GET request, so an example for your implementation would be:
request.GET.getlist("brand_id")
or
request.GET.get("brand_id")
Mainly, for what I wanted to do, it's as easy as two steps.
First, add a column in list_display to show the link to the add_view:
Brand.py
#admin.display(description='Link', ordering='url')
def get_add_link(self):
url = f"{reverse('admin:myapp_car_add')}?brand={self.id}"
return format_html(f"<a href='{url}'><i class='fas fa-plus'></i></a>")
Note that I add the URL as the reverse of admin:<app>_<model>_<action> and pass the brand ID as parameter.
CarAdmin.py
def get_changeform_initial_data(self, request):
brand_id = request.GET.get('brand')
return {'brand': brand_id}
By adding this method in the CarAdmin class, I am able to set the brand as the default value.
I did not have to do anything with the Model. The only change needed was in the ModelAdmin:
def get_changeform_initial_data(self, request):
brand = None
brand_id = request.GET.get('brand')
if (brand_id):
brand = Brand.objects.get(id=brand_id)
return {'brand': brand}
Docs link here: https://docs.djangoproject.com/en/4.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_changeform_initial_data
using CreateView class, I want to save multiple data entries.
example inputs:
item is "apple,banana,carrots"
location is "location 1"
I want to save them to database like this:
[apple, location 1]
[banana, location 1]
[carrots, location 1]
#model.py
class Inventory(models.Model):
item = models.CharField(max_length=14)
location = models.CharField(max_length=10)
#forms.py
class InventoryCreateForm(forms.ModelForm):
item = forms.CharField(widget=forms.Textarea(attrs={'rows': 8,
'cols': 14}))
class Meta:
model = Inventory
#views.py
class InventoryCreateView(CreateView):
model = Inventory
form_class = InventoryCreateForm
Thank you
You need to override the "form_valid()" method used by the createview.
You then need to read in the form data
def form_valid(self,form):
self.object = form.save(commit=False)
foo = self.object.bar #your data is in the object
Then because you are using a textfield you need to somehow split the data passed into the form and loop over those values. Ideally you will want a list of items ['apple', 'banana', 'pear']
then take the location out of the list and store that into a variable that can be used later on location_variable.
Once you have the data in the form you want you then need to instantiate the Inventory model
from foo.models import Inventory #import at the top of your file
for item is list:
inventory = Inventory()
inventory.item = item
inventory.location = location_variable
inventory.save()
I hope this answer can help you in some way, if you would like further details on Class based view, visit ccbv where all of the information for each view is listed.
Otherwise you can look in the django Form docs for a more suitable form to use.
hopefully this helps anyone else who comes to this page.
Here is what I did:
class TrainingBulkCreateForm(ModelForm):
# todo: could we make the original user object a multiple choice field instead?
extra_user = forms.ModelMultipleChoiceField(queryset=User.objects.filter(is_active=True), required=True)
class Meta(object):
model = Training
fields = '__all__'
def post(self, request, *args, **kwargs):
result = super().post(request, *args, **kwargs)
users = request.POST.getlist('extra_user')
if users:
# modify request.POST data then re-save the additional users
for user in users:
# Need to copy the data because the initial QueryDict is immutable data
postdata_copy = request.POST.copy()
postdata_copy['user'] = user
form2 = TrainingBulkCreateForm(postdata_copy)
form2.save()
return result
I have my item field (mine is user) split into two form fields - the original item which is 1 object like apple.
Then I also have extra_user as a second form field. This takes multiple objects like [banana, carrots]
In the view I invoke the super().post(...) to save the initial apple object.
Then I check for the extra_user field to see if there are multiple foods. If there are extras, I copy the immutable QueryDict object request.POST as postdata_copy.
Then I modify postdata_copy so instead of having apple we replace with banana. (This basically just duplicates the apple form into a new copy with banana and re-saves the form). Then we loop again and replace apple with carrots.
Note that my Form object uses ModelMultipleChoiceField for extra_user object. This has better data integrity than typing in raw text.