Populate a field on changeform_view - django-admin

When I open an item in a model, for example: /shop_push/shopapidefinition/4/change/ I want to populate one field with my own value and leave the rest intact.
I can currently do something when I edit, like below
class ShopApiDefinitionAdmin(admin.ModelAdmin):
def __init__(self, *args, **kwargs):
# let's define this so there's no chance of AttributeErrors
self._request = None
super(ShopApiDefinitionAdmin, self).__init__(*args, **kwargs)
def get_request(self):
return self._request
def changeform_view(self, request, *args, **kwargs):
# stash the request
self._request = request
print "changelist_view ShopApiDefinitionAdmin"
# call the parent view method with all the original args
# ShopApiDefinitionAdmin['about_the_shop'] = "hello"
return super(ShopApiDefinitionAdmin, self).changeform_view(request, *args, **kwargs)
What I don't know how to do it add data to a field for that record, for example
field['about_the_shop'] = "new text"
It would be nice to save the new data, but that isn't essential.
Any guidance would be appreciated
Thanks

I realised that it was probably best to use change_view and that this triggers before the content is retrieved from the model, so what I do is used the object_id, query the record, do my code and then save the details
def change_view(self, request, object_id, form_url='', extra_context=None):
# Get the record
obj = ShopApiDefinition.objects.get(id=object_id)
# get a value as I use that in my code
self.password = obj.password
# do some code
shopify_shop = "a value from my code"
obj.about_the_shop = shopify_shop # change field
obj.save() # this will update only
return super(ShopApiDefinitionAdmin, self).change_view(
request, object_id, form_url, extra_context=extra_context,
)
That seems to work. It saved the value in the model, and when the form loads it is populated with both the existing data and then new data from my code
Thanks
Grant

Related

How to append an array in parameters after Rails form submit?

I have a form with checkboxes that get passed as an array "list_person_ids" on form submit. My models are "Occurance" which has an n:m relationship with "ListPerson" through the Model "Person". "list_person_ids" are saved in Person with the Occurance id and the ListPerson id.
I want to append one or more values to the array before this gets saved. The reason I need to do this is because the user can also add a new value in ListPerson using a textbox with the name "person_name".
def create
#occurance = Occurance.new(occurance_params)
add_person_id(#occurance)
...
# save
end
def add_person_id(object)
if params[:person_check] == '1'
object.list_person_ids.push( ListPerson.find_or_create_by(person: params[:person_name]).id )
end
end
def occurance_params
params.require(:occurance).permit(:person_check, :person_name, dim_person_ids: [])
end
find_or_create_by is successful, but nothing gets pushed to the array "list_person_ids". There is also no error message. Do I have to give permission somewhere to append this array? Please let me know if some information is missing.
on your model you can do something like below:
....
before_create :create_lists
private
def create_lists
ListPerson.find_or_create_by(list_person_ids: person_name.id)
end

django rest framework save serializer fail with post method

I'm trying to update a profile object with post method, but I get a error message when trying to save my serializer :
You cannot call `.save()` after accessing `serializer.data`.If you need to access data before committing to the database then inspect 'serializer.validated_data' instead.
My view :
class SettingsProfileView(APIView):
"""
Get and update user profile
"""
queryset = models.UserProfile.objects.all()
serializer_class = serializers.UserProfileSerializer
renderer_classes = [TemplateHTMLRenderer]
template_name = 'base_/settings/profile.html'
def get_object(self, pk):
try:
return models.UserProfile.objects.get(pk=pk)
except models.UserProfile.DoesNotExist:
raise Http404
def get(self, request, format=None):
if not request.user.is_authenticated:
return Response({"error": _("User is not connected")}, status=status.HTTP_511_NETWORK_AUTHENTICATION_REQUIRED)
try:
profile = request.user.profile
except models.UserProfile.DoesNotExist:
profile = models.UserProfile(user=request.user)
profile.key_name = request.user.username
profile.save()
profile = self.get_object(request.user.profile.id)
serializer = serializers.UserProfileSerializer(profile)
return Response({'serializer': serializer, 'profile': profile})
def post(self, request, format=None):
serializer = serializers.UserProfileSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
The error occured in this part : serializer.save() in my post method. Is it because the serializer is accessing data in his instentiation method ?
My serializer is very basic, it has no special code :
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = ('user', 'coachs', 'is_coach', 'gender', 'weight', 'height', 'visibility', )
Maybe the problem comes from the fact that I'm using post methode instead of update ?
EDIT (after #pleasedontbelong post) :
I've tried with generic view :
class SettingsProfileView(generics.GenericAPIView):
but the update method is not fired (because I come from a HTML post), so I had to manually raise update method like that :
def post(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object(request.user.profile.id)
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
return Response(serializer.data)
But the error is still the same.
I cannot find any example where an object is updated with django rest by post method. Is it because it's not a good way to proceed ?
After hours of debugging, it appear that the problem comes from Visual Studio's breakpoints. After removing breakpoints it works fine.
Maybe Visual Studio try to read in serializer.data and then affects them.
It's better to use generic views, it prevents you from rewriting all this code. But if you rather do it manually, you can always check the source code to check how it's done:
https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/mixins.py#L61-L78
class UpdateModelMixin(object):
"""
Update a model instance.
"""
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
return Response(serializer.data)
def perform_update(self, serializer):
serializer.save()
def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
you must pass the UserProfile instance to the serializer
BTW: you should/must use PUT or PATCH for updating, POST is for creating objects.
Hope this helps :)

Rails Handling Nil in Class Method Chaining

I have the following Search class method that takes a bunch of params and then builds a request.
def self.search(agent, params)
RealPropertySale.where(id: available_ids)
.joins(:address)
.by_state(state)
.by_suburb(suburb)
.by_post_code(post_code)
.by_country(country)
.paginate(page: page)
end
def self.by_state(state)
where(addresses: {state: state})
end
def self.by_suburb(suburb)
where(addresses: {suburb: suburb})
end
def self.by_post_code(post_code)
where(addresses: {post_code: post_code})
end
def self.by_country(country)
where(addresses: {country: country})
end
What is the correct way of handling if one of my custom class methods e.g. self.by_country(country) returns nil so that the query continues with whatever param/s are present. I have tried returning self if one the params is blank but the query is lost and the class is returned resulting in errors.
I agree with #Michael Gaskill that you probably should only call the scopes that actually affect the final query (i.e. that have meaningful params).
However, if you insist on the scopes ignoring nil parameters, you may make them return current_scope instead (which is an undocumented but useful method):
def self.by_state(state)
return current_scope if state.nil?
where(addresses: {state: state})
end
We did something similar by breaking it down like so :
response = RealPropertySale.where(id: available_ids)
.joins(:address)
response = response.by_state(state) if state
response = response.by_suburb(suburb) if suburb
response = response.by_post_code(post_code) if post_code
response = response.by_country(country) if country
response = response.paginate(page: page) if page
I like the readibility. I try to break it down to as many pieces as needed, but this should be adapted to your business logic. Don't know if, for you, it makes sense to check if suburb is provided for example.

Saving my rails object from an array of values sourced form a csv

i'm currently trying to update attributes via a csv upload.
My method looks like this:
def upload_csv
CSV.parse(params[:file].read, headers: true) do |row|
foo = Foo.find_by_id(row.to_hash["id"])
row.to_hash.each do |v|
if Foo.new.has_attribute?(v[0]) && v[0] != "id"
foo.update_attributes()
end
end
end
end
When it jumps into where I want to update my attributes, i'm getting an array that looks like this:
["bar", "22"]
How can I save that value to my foo object?
Ok, so reading you're code I'm concluding that your problem is really that you have a CSV that may contain some fields that are not in your model:
def upload_csv
excluded = %w/id created_at updated_at/
CSV.new( params[:file], headers: true) do |row|
rh = row.to_hash
foo = Foo.find_by id: rh['id']
foo.update! rh.slice(*foo.attribute_names).except(*excluded)
end
end
Note that I'm assuming params[:file] is an uploaded file from a form, in which case it's an IO object, and so can be passed into CSV.new directly (no need to read it all into memory and pass it to CSV.parse).

Dynamically excluding fields in inline django admin

I want to exclude some fields in my inline based on my request user.
I know somehow I can handle this with methods like 'get_formsets', 'add_view', 'change_view', but I'm not sure what the syntax is.
Any suggestions?
I achieved what I needed with the next code in my inline class:
def get_formset(self, request, obj=None, **kwargs):
if request.user.groups.all().count() > 0:
if request.user.groups.all()[0].name == 'User Group Name':
kwargs['exclude'] = ['field_to_exclude',]
return super(MyInline, self).get_formset(request, obj, **kwargs)
The answer to this question gave me the hints: different fields for add and change pages in admin
There's also the get_exclude hook:
class FoodInline(TabularInline):
model = Food
def get_exclude(self, request, obj=None):
group = request.user.groups.first()
if group and group.name == 'User Group Name':
return ['field_to_exclude', ]
return self.exclude

Resources