Tastypie resource name clash - django

I am currently using tastypie with 2 apps. Each of those apps has a model called Group. They operate very differently, and the only similarity is the name 'Group'.
When only one or the other app is added to the urls file, then it works like a charm. However, as soon as I add both apps, then there's a name clash, and the get_resource_uri() method returns the wrong string. Here is some code:
urls.py
from myapp1.resources import GroupResource as gr_a
from myapp2.resources import GroupResource as gr_b
myapp1_api = Api(api_name='1.0')
myapp1_api.register(gr_a())
myapp2_api = Api(api_name='1.0')
myapp2_api.register(gr_b())
on line 37 of the current api.py file in the tastypie repo I see this code:
if resource_name is None:
raise ImproperlyConfigured("Resource %r must define a 'resource_name'." % resource)
self._registry[resource_name] = resource
Since both of my Group resources have the resource_name of 'group', they get registered on top of each other, even though they are registered at separate urls. Apart from changing the actual resource name, is there a way around this name clash?
Update
The uris would look something like this:
/myapp1/1.0/group/
/myapp2/1.0/group/
Ideally I don't want myapp1 and myapp2 to know about each other (ie the Group class is distinct). The workaround for this is to change myapp2.Group to myapp2.MyGroup (to avoid the name clash), but its really not that elegant.

In all my resources I had the resource_name blank, since I was happy with the default name. Also I wanted a url such as /myapp/1.0/group/ and not /myapp/1.0/myapp/group/
What I've now done is change all the resource_name attributes to the form "myapp/group" and bound them all to an empty url. This gave me a nice such as: /1.0/myapp/group/ while making sure there are no name clashes in the resources.

Related

reversing admin "change" url

I´m trying to find some example of how to use change url of modelAdmin and what exactly result it gives. Sincerly I do not understand the part from docs:
"This will find the first registered instance of the admin application (whatever the instance name), and resolve to the view for changing poll.Choice instances in that instance."
You can reverse with:
reverse('app:poll_choice_change', kwargs={'object_id': pk})
With app the default namespace (you can specify a different one if you include this in the path(…) where you attach the admin.urls. poll the name of the app, choice the name of the model in lowercase, and pk the primary key of the object.

Using drf-nested-routers with nested HyperlinkedIdentityFields

I am trying to generate nested HATEOAS links in a serializer using the drf-nested-routes package. My current setup would be as follows:
/resource_a/<pk>
/resource_a/<pk>/resource_b/<pk>
/resource_a/<pk>/resource_b/<pk>
/resource_a/<pk>/resource_b/<pk>/resource_c
I am unable to create a HyperlinkedIdentityField that points to the last route. According to the documentation, one can create hyperlinked fields like this:
nameservers = HyperlinkedIdentityField(
view_name='domain-nameservers-list',
lookup_url_kwarg='domain_pk'
)
Or
nameservers = NestedHyperlinkedRelatedField(
many=True,
read_only=True, # Or add a queryset
view_name='domain-nameservers-detail'
parent_lookup_url_kwargs={'domain_pk': 'domain__pk'}
)
But these approaches fail when trying to reach a resource that is 2 layers deep in the URL hierarchy. The first method is not compatible, as it does not allow to add a second lookup_url_kwarg, and as for the second one, it throws an exception (ImproperlyConfigured) when configuring with the (in my opinion) proper attributes (resource_a__pk, resource_b__pk).
Is this at all possible with this package? Otherwise I will resort to a simpler solution using a SerializerMethodField:
resource_c = serializers.SerializerMethodField()
def get_resource_c(self, obj):
url = reverse('resource_b-resource_c-list', kwargs=dict(resource_a_pk=obj.resource_a.pk, resource_b_pk=obj.pk))
return self.context['request'].build_absolute_uri(url)
Thanks in advance!
I've done this before using NestedHyperlinkedRelatedField and it definitely works. My guess is that your configuration is not correct. One thing I noticed is that you use parent_lookup_url_kwargs while in my case I use parent_lookup_kwargs.
Based on your explanation I think it needs to look like this
NestedHyperlinkedRelatedField(...,
parent_lookup_kwargs={
'resource_a_pk': '<how to reach resource_a pk from resource_b>'})

Django context processors and URL arguments

I have some code that is repeated at the start of my Django views. It basically just adds some variables to the context, but based on the URL argument, e.g.
def someView(request, id):
target = Target.objects.get(id=id)
# name will be added to ctx
name = target.name
(there are more attributes added and other attributes from related models, but this gives the general idea --- There are quite a few lines of repeat code at the start of each view)
I thought I could make my code more DRY by taking advantage of Django's context processors, but it would seem these don't access to the URL arguments?
Is there another way to avoid these repeat lines? Maybe middleware or something else?
You can access the URL parameters via request through the resolver_match attribute. So for instance you can do request.resolver_match.kwargs['id'] to get the ID kwarg.

Creating unique URL/address for a resource to share - Best practices

In my application there is a need to create unique URLs (one per resource) that can be shared. Something like Google Calendar Private address for a calendar. I want to know what are the best practices for this.
If it helps my application is in Django.
Please let me know if this question needs more explanation.
This should be very straightforward. In your urls.py file you want a url like this:
url(r'/resource/(?P<resource_name>\w+)', 'app.views.resource_func', name="priv-resource"),
Then you handle this in views.py with a function called:
def resource_func(request, resource_name):
# look up resource based on unique string resource_name...
Finally, you get to use this in your templates too, using naming:
{% url priv-resource string %}
Just ensure that in your models.py:
class ResourceModel(models.Model)
resource_name = models.CharField(max_size=somelimit, unique=True)
I might even be tempted to use a signal handler to generate this field automatically upon save of the object. See the documentation.

django admin filter tweaking

I want to use django's admin filter on the list page.
The models I have are something like this:
class Location(model):
name = CharField()
class Inquiry(Model):
name = CharFiled()
location = ManyToManyField(Location)
Now I want to filter Inquiries, to display only those that contain relation to specific Location object. If I use
class InqAdmin(ModelAdmin):
list_filter = ['location', ]
admin.site.register(Inquiry, InqAdmin)
the admin page displays me the list of all Locations and allows to filter.
What I would like to get, is to get list of only those locations that have some Inquiries in relation to them (so I don't ever get the empty list result after filtering).
How can this be done?
You could create a custom manager for Locations that only returns Locations that have an Inquiry associated with them. If you make this the default manager, the admin will use it.
Only caveat is that you'll need create another manager that returns all Locations and use that in the rest of your app whenever you want to retrieve Locations that don't have an associated Inquiry.
The managers section in the Django docs is quite good, and should be all you need to get this set up.
EDIT:
sienf brings up a good point. Another way to accomplish this would be to define a subclass of django.contrib.admin.SimpleListFilter, and write the queryset method to filter out Inquiries with empty Locations. See https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter