black_hole_sun
black_hole_sun

Reputation: 956

Django: How to check a Form with a m2m relation object already exists or is “unique_together”?

I am testing forms and nesting models in django. In my Project a Person can enter departure, arrival (city names) and choose a weekly day (Mon-Fri). Maybe he drives every “Tuesday” from Amsterdam to Paris. I wanted this constellation to be unique – just for fun. So If another user enters the same route the relation should be linked to the same Car.object.

Models.py

class Person(models.Model):
    name = models.CharField(max_length=255, blank=False, unique=True)
    route = models.ManyToManyField('Car')
    def __str__(self):
        return self.name


class Car(models.Model):
    name = models.CharField(max_length=255, blank=False, unique=True)
    weekdays = models.ForeignKey('Week', null=True, blank=False, on_delete=models.SET_NULL)
    departure = models.CharField(max_length=255, blank=False)
    arrival = models.CharField(max_length=255, blank=False)

    class Meta:
        unique_together = ['weekdays', 'departure', 'arrival'] # --- Unique combination

    def __str__(self):
        return self.name

class Week(models.Model):
    day = models.CharField(max_length=255, blank=False, unique=True)

    def __str__(self):
        return self.day

views.py

class RouteCreateView(CreateView):
    model = Person
    template_name ="testa/create_route.html"
    form_class = RouteForm
    success_url = reverse_lazy('testa:testa_home')

    def form_valid(self, form):
        return super().form_valid(form)

forms.py

class RouteForm(forms.ModelForm):
    # --- apply ChoiceField
    day = forms.ModelChoiceField(queryset=None)
    car_name = forms.CharField()
    departure = forms.CharField()
    arrival = forms.CharField()

    class Meta:
        model = Person
        fields = [ 
            'name'
            ]


    def __init__(self, *args, **kwargs):
        super(RouteForm, self).__init__(*args, **kwargs)
        self.fields['day'].queryset = Week.objects.all()


    def save(self, commit=True):
        personData = super().save(commit)
        data = self.cleaned_data
        carData = Car(name=data['car_name'], weekdays=data['day'], departure=data['departure'], arrival=data['arrival'])
        if commit:
            carData.save()
            personData.route.add(carData)  # --- save m2m relation
        return personData

If i enter two times for example „“Tuesday” from Amsterdam to Paris “ then an Error Message appears obviously, this error message (it´s german), telling me I have a double entry / Key.


Question

So my save()Method does not work because I need some kind of logic, so that Django takes the existing car.object or creates a new - if it is not a double entry. But I do not know where to start? The easiest way would be to get some kind of response from my model meta option Car.unique_together so "if it´s an “double-key error” then take the existing object". Is there a way to fetch the response? And what kind of Values it would be, only errors, could not find any hint in the doc? Or should I try some logic with exists()

That was my kind of idea / approach of a new save() 😊

def save(self, commit=True):
    personData = super().save(commit)
    data = self.cleaned_data
    carData = Car(name=data['car_name'], weekdays=data['day'], departure=data['departure'], arrival=data['arrival'])
    if commit:
        # Check if database sends unique_together response
        # if yes 
        if Car.Meta.unique_together is True:
            getAlternative = Car.object.get(Meta.unique_together) # --- get the object which already exist
            personData.route.add(getAlternative)  # --- save m2m relation
        # if not 
        else:
            carData.save()  # --- save object
            personData.route.add(carData)  # --- save m2m relation
    return personData

obviously i get a error message: type object 'Car' has no attribute 'Meta'

Upvotes: 0

Views: 92

Answers (1)

WiRai
WiRai

Reputation: 751

Theres get_or_create for such use case: https://docs.djangoproject.com/en/2.2/ref/models/querysets/#get-or-create

...
car, created = Car.objects.get_or_create(
    weekdays=data['day'],
    departure=data['departure'],
    arrival=data['arrival'],
    defaults = dict(name=data['car_name']),
)
personData.route.add(car)
...

Obviously given name gets ignored if another car with same weekdas, departure, arrival has been found.

I suggest to put the code for creating the car and adding the route in a transaction.atomic() https://docs.djangoproject.com/en/2.2/topics/db/transactions/#django.db.transaction.atomic

Upvotes: 1

Related Questions