1

My application has the following structure:

models.py

class EventHost(models.Model):

    hostid = models.ForeignKey(Host, on_delete=models.PROTECT)
    eventid = models.ForeignKey(Event, on_delete=models.CASCADE)
    someparam = models.CharField(max_length=100, blank=True, null=True)

    ...

    class Meta:
        unique_together = ("hostid", "event")


class Host(models.Model):
    hostid = models.IntegerField(primary_key=True)
    hostname = models.CharField(max_length=100)
    ...


class Event(models.Model):
    eventid = models.AutoField(primary_key=True)
    eventname = models.CharField(max_length=100)
    ...
    hosts = models.ManyToManyField(Host, through='EventHost')

views.py

class EventViewSet(viewsets.ModelViewSet):
    queryset = Event.objects.order_by('-date')
    serializer_class = EventSerializer

class HostViewSet(viewsets.ModelViewSet):
    queryset = Host.objects.order_by('-hostid')
    serializer_class = HostSerializer

class EventHostViewSet(viewsets.ModelViewSet):
    queryset = EventHost.objects.all()
    serializer_class = EventHostSerializer

Currently to update EventHost table I'm doing http put providing id (which is primary key) in my url .

I'd like to be able to update EventHost providing hostname and eventname (which will be passed in url) instead of id.

Using SQL it would look like this:

update eventhost set someparam='somevalue' from eventhost as a, event as b, host as c where b.id = a.eventid and c.hostid = a.hostid and b.eventname='myevent' and c.hostname='myhost';

From documentation I understood that I would need to modify the default update method for the viewset or/and modify queryset. Any ideas how should it be achieved?

2 Answers 2

1

You can override get_object method and use your parameters provided from url like that:

class EventHostViewSet(viewsets.ModelViewSet):
    queryset = EventHost.objects.all()
    serializer_class = EventHostSerializer

    def get_object(self):
       return EventHost.objects.get(hostid=self.kwargs['hostid'],eventid=self.kwargs['eventid'])

In this way, you must manage if there is no record with this query scenario, as custom

Sign up to request clarification or add additional context in comments.

Comments

1

Assuming that you have properly constructed URL.

Edited EventHostViewSet.get_object method:

class EventHostViewSet(viewsets.ModelViewSet):
    ...
    lookup_field = 'eventname'
    def get_object(self):
        queryset = self.filter_queryset(self.get_queryset())

        filter_kwargs = {'hostid__hostname': self.kwargs.get('hostname'),
                         'eventid__eventname': self.kwargs.get('eventname')}
        obj = get_object_or_404(queryset, **filter_kwargs)

        self.check_object_permissions(self.request, obj)
        return obj

EventHostViewSet registration:

router.register(rf'event-hosts/(?P<hostname>[.]+)', EventViewSet)

Some comments about your problem:

  • You will have problem when in your system will exist two EventHost with same hostid__hostname and eventid__event because queryset get method should only return ONE record
  • in DRF PUT method needs all fields to be provided in request data, if you want to update selected fields you should use PATCH method or override update viewset method (set partial to True)

EDITED AGAIN:

This is really bad design, you should not do this like that (somehow you should use id / maybe @action decorator to construct specific url for updating like that).

4 Comments

Thanks. How should I construct it in urls.py? Currently it looks like this: router = routers.DefaultRouter() router.register(r'accounts', AccountViewSet) router.register(r'events', EventViewSet) router.register(r'hosts', HostViewSet) urlpatterns = [ url(r'^api/v1/', include(router.urls)), ] Can I use some regex in route.register?
I edited answer again. This is really bad design (but it resolves your problem - workaround) and you should use diffrent solution.
Could you give me an example of url that should be used here? I'm still struggling to make it work
.../event-hosts/SOMEHOSTNAME/SOMEEVENTNAME

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.