0

I have one model Detail which has OneToOne relation with default User Model. I have a field FileField in my Detail model, where I want to upload the files using forms from frontend/templates.

I have been working around it a lot but I am not getting it done. I need help, please.

My models.py is:

from django.db import models
from django.contrib.auth.models import User

class Detail(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    file = models.FileField(verbose_name="CSV File", upload_to='csv_files/')
    file_desc = models.TextField("CSV File Description")

    def __str__(self):
        return ("{} ({} {})".format(self.user.email, self.user.first_name, self.user.last_name))

My forms.py is:

from django.forms import ModelForm
from .models import Detail

class DetailForm(ModelForm):
    class Meta:
        model = Detail
        fields = ['file', 'file_desc']

My views.py is:

from django.views import View

class UserAPI(View):
    template_name = 'accounts/user.html'

    def get(self, request):
        form = DetailForm(instance=request.user)

        context = {'form': form}
        return render(request, self.template_name, context)

    def post(self, request):
        form = DetailForm(request.POST, request.FILES, instance=request.user)
        if form.is_valid():
            form.save()
            return redirect('user')

        context = {'form': form}
        return render(request, self.template_name, context)

and my user.html (template) is:

<form method="post" enctype="multipart/form-data">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Submit</button>
</form>

Every time I go to localhost:8000/user and fill the form and click on Submit Button, it gives me following error on frontend: No File Chosen and also the following statement appears above the File Selection Button: This field is required.

I shall appreciate for the help. Thanks

UPDATE:

urls.py

urlpatterns = [
    path('register', RegisterAPIHTML.as_view(), name='register'),
    path('login', LoginAPIHTML.as_view(), name='login'),
    path('user', UserAPI.as_view(), name='user'),
    path('logout', LogoutAPI.as_view(), name='logout'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

settings.py

STATIC_ROOT  =   os.path.join(BASE_DIR, 'staticfiles')

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)

MEDIA_ROOT = os.path.join(BASE_DIR, 'data/')
MEDIA_URL = '/media/'
Neha AK
  • 67
  • 1
  • 10
  • 2
    Make sure your form tag is as follows: `
    `
    – raphael Apr 04 '22 at 13:40
  • Thank you @raphael I did that what you said and I even added `instance=request.user` in this line `form = DetailForm(request.POST, request.FILES)` in the `post()` method of `UserAPI` class, now it does not show errors, but it also does not do anything else, I checked the `Detail` table in admin interface and it was empty. – Neha AK Apr 04 '22 at 14:16
  • Hi @raphael I added `print(form.errors)` in the `else` block of the `if` statement that is checking the validity of form. But nothing is printed on the console except `[04/Apr/2022 19:40:00] "GET /user HTTP/1.1" 200 602`. Also I added `print('Success')` statement before the `if form.is_valid()` to see if I am successfully executing to the if statement, and that is also not printed. P.S. I changed `instance=request.user` to `initial={'user': request.user}` but still all in vain, no errors printed, no 'Success' printed. – Neha AK Apr 04 '22 at 14:43
  • Give `/` at the end of your folder path that is `upload_to="csv_files/"`, as stated in the docs https://docs.djangoproject.com/en/4.0/ref/models/fields/#filefield – Sunderam Dubey Apr 04 '22 at 14:44
  • Hi @SunderamDubey I added `/` at the end of `upload_to="csv_files/"` in `Detail` model, but no change, the behaviour is same as I described in my earlier comments. – Neha AK Apr 04 '22 at 14:47
  • Are you not able to find your uploaded file in the `csv_files` folder, is that your issue? – Sunderam Dubey Apr 04 '22 at 14:47
  • Answer to you question is yes, @SunderamDubey, but its more than that, I am not even seeing any record in `Detail` Table as well, a record should have the `user`, `file`, and `file_desc`. – Neha AK Apr 04 '22 at 14:55
  • You need to use `CreateView` rather than `View`. – Sunderam Dubey Apr 04 '22 at 14:56
  • I changed the `View` to `CeateView`, but no changes in behaviour. – Neha AK Apr 04 '22 at 15:02
  • Update the full traceback including `urls.py` and `settings.py`. – Sunderam Dubey Apr 04 '22 at 15:10
  • PLease check my question @SunderamDubey. I have added the update as you said, but there is no `Traceback` error in console and frontend, actually there is no error at all, just the file would not get uploaded. – Neha AK Apr 04 '22 at 16:13
  • Can you update the question to show how the code looks now. I am having trouble seeing how are you passing the user instance to the form. – vinkomlacic Apr 04 '22 at 20:47
  • Hi @vinkomlacic I just updated the code, please have a look. – Neha AK Apr 04 '22 at 21:07

1 Answers1

2

I think your form is not saving anything because the line with form.is_valid() returns False.

You won't see anything in the form.errors because that attribute only shows field errors, not non-field errors. The non-field error in this case is that the required field user is missing. This is because you haven't specified it in the model form (also why it gets placed as a non-field error).

Also, you're not rendering the non-field errors in the HTML which is why you don't see them in your page after the post submission. I suggest using crispy-forms package (here's the link). It handles things like form rendering out of the box.

Moreover, specifying argument ìnstance=request.user is incorrect because if you have a DetailForm the instance should be a model instance of the Detail model, not User model. Keep in mind, instance kwarg is only necessary if you want to use this form for updating.

I suggest an implementation similar to the following:

forms.py

class DetailForm(ModelForm):
    class Meta:
        model = Detail
        fields = ['user', 'file', 'file_desc']

        widgets = {
            # I'm guessing you don't want to see the User field
            'user': HiddenInput()
        }

Relevant part of views.py

def get(self, request):
    form = DetailForm()

    context = {'form': form}
    return render(request, self.template_name, context)

def post(self, request):
    form = DetailForm(request.POST, request.FILES, initial={'user': request.user.id})
    if form.is_valid():
        form.save()
        return redirect('user')


    context = {'form': form}
    return render(request, self.template_name, context)

To make this a complete answer, I will include a comment from @raphael which fixed the first issue of your code:

Make sure your form tag is as follows: <form method="post" enctype="multipart/form-data">

Some resources to follow:

vinkomlacic
  • 1,822
  • 1
  • 9
  • 19