diff --git a/mo/imports.py b/mo/imports.py
index bc09f3b0b99c64b3720788bba4e5a3f3dace7654..005f62f029da3f0a8840b9c16d462352ce53b38a 100644
--- a/mo/imports.py
+++ b/mo/imports.py
@@ -188,7 +188,7 @@ class Import:
 
         return r
 
-    def find_or_create_user(self, email: str, krestni: str, prijmeni: str, is_org: bool) -> Optional[db.User]:
+    def find_or_create_user(self, email: str, krestni: Optional[str], prijmeni: Optional[str], is_org: bool) -> Optional[db.User]:
         try:
             user, is_new = mo.users.find_or_create_user(email, krestni, prijmeni, is_org, reason='import')
         except mo.CheckError as e:
@@ -212,7 +212,7 @@ class Import:
 
         return pts
 
-    def find_or_create_participant(self, user: db.User, year: int, school_id: int, birth_year: int, grade: str) -> Optional[db.Participant]:
+    def find_or_create_participant(self, user: db.User, year: int, school_id: Optional[int], birth_year: Optional[int], grade: Optional[str]) -> Optional[db.Participant]:
         try:
             part, is_new = mo.users.find_or_create_participant(user, year, school_id, birth_year, grade, reason='import')
         except mo.CheckError as e:
@@ -433,28 +433,23 @@ class ContestImport(Import):
         assert isinstance(r, ContestImportRow)
         num_prev_errs = len(self.errors)
         email = self.parse_email(r.email)
-        krestni = self.parse_name(r.krestni)
-        prijmeni = self.parse_name(r.prijmeni)
-        school_place = self.parse_school(r.kod_skoly)
-        rocnik = self.parse_grade(r.rocnik, (school_place.school if school_place else None))
-        rok_naroz = self.parse_born(r.rok_naroz)
+        krestni = self.parse_name(r.krestni) if r.krestni else None
+        prijmeni = self.parse_name(r.prijmeni) if r.prijmeni else None
+        school_place = self.parse_school(r.kod_skoly) if r.kod_skoly else None
+        rocnik = self.parse_grade(r.rocnik, (school_place.school if school_place else None)) if r.rocnik else None
+        rok_naroz = self.parse_born(r.rok_naroz) if r.rok_naroz else None
         misto = self.parse_opt_place(r.kod_mista, 'místo')
         oblast = self.parse_opt_place(r.kod_oblasti, 'oblast')
 
         if (len(self.errors) > num_prev_errs
-                or email is None
-                or krestni is None
-                or prijmeni is None
-                or school_place is None
-                or rocnik is None
-                or rok_naroz is None):
+                or email is None):
             return
 
         user = self.find_or_create_user(email, krestni, prijmeni, is_org=False)
         if user is None:
             return
 
-        part = self.find_or_create_participant(user, mo.current_year, school_place.place_id, rok_naroz, rocnik)
+        part = self.find_or_create_participant(user, mo.current_year, school_place.place_id if school_place else None, rok_naroz, rocnik)
         if part is None:
             return
 
diff --git a/mo/users.py b/mo/users.py
index f7d789c73f28f501dce83274d97e28cd4dee110e..251fb3c4c920adfcffed8a6a44ccac7219f69455 100644
--- a/mo/users.py
+++ b/mo/users.py
@@ -52,11 +52,13 @@ def validate_and_find_school(kod: str) -> db.Place:
     return place
 
 
-def find_or_create_user(email: str, krestni: str, prijmeni: str, is_org: bool, reason: str) -> Tuple[db.User, bool]:
+def find_or_create_user(email: str, krestni: Optional[str], prijmeni: Optional[str], is_org: bool, reason: str) -> Tuple[db.User, bool]:
     sess = db.get_session()
     user = sess.query(db.User).filter_by(email=email).one_or_none()
     is_new = user is None
     if user is None:  # HACK: Podmínku je nutné zapsat znovu místo užití is_new, jinak si s tím mypy neporadí
+        if not krestni or not prijmeni:
+            raise mo.CheckError('Osoba s daným emailem zatím neexistuje, je nutné uvést její jméno.')
         user = db.User(email=email, first_name=krestni, last_name=prijmeni, is_org=is_org)
         sess.add(user)
         sess.flush()    # Aby uživatel dostal user_id
@@ -67,7 +69,7 @@ def find_or_create_user(email: str, krestni: str, prijmeni: str, is_org: bool, r
             details={'action': 'create-user', 'reason': reason, 'new': db.row2dict(user)},
         )
     else:
-        if user.first_name != krestni or user.last_name != prijmeni:
+        if (krestni and user.first_name != krestni) or (prijmeni and user.last_name != prijmeni):
             raise mo.CheckError(f'Osoba již registrována s odlišným jménem {user.full_name()}')
         if (user.is_admin or user.is_org) != is_org:
             if is_org:
@@ -77,11 +79,17 @@ def find_or_create_user(email: str, krestni: str, prijmeni: str, is_org: bool, r
     return user, is_new
 
 
-def find_or_create_participant(user: db.User, year: int, school_id: int, birth_year: int, grade: str, reason: str) -> Tuple[db.Participant, bool]:
+def find_or_create_participant(user: db.User, year: int, school_id: Optional[int], birth_year: Optional[int], grade: Optional[str], reason: str) -> Tuple[db.Participant, bool]:
     sess = db.get_session()
     part = sess.query(db.Participant).get((user.user_id, year))
     is_new = part is None
     if part is None:
+        if not school_id:
+            raise mo.CheckError('Osoba s daným emailem zatím není zaregistrovaná do ročníku, je nutné uvést školu.')
+        if not birth_year:
+            raise mo.CheckError('Osoba s daným emailem zatím není zaregistrovaná do ročníku, je nutné uvést rok narození.')
+        if not grade:
+            raise mo.CheckError('Osoba s daným emailem zatím není zaregistrovaná do ročníku, je nutné uvést ročník.')
         part = db.Participant(user=user, year=year, school=school_id, birth_year=birth_year, grade=grade)
         sess.add(part)
         logger.info(f'{reason.title()}: Založen účastník #{user.user_id}')
@@ -91,9 +99,9 @@ def find_or_create_participant(user: db.User, year: int, school_id: int, birth_y
             details={'action': 'create-participant', 'reason': reason, 'new': db.row2dict(part)},
         )
     else:
-        if (part.school != school_id
-                or part.grade != grade
-                or part.birth_year != birth_year):
+        if ((school_id and part.school != school_id)
+                or (grade and part.grade != grade)
+                or (birth_year and part.birth_year != birth_year)):
             raise mo.CheckError('Účastník již zaregistrován s odlišnou školou/ročníkem/rokem narození')
     return part, is_new
 
diff --git a/mo/web/fields.py b/mo/web/fields.py
index 1a931335a4e06fcc40cb3c985c5c99a32caecf68..13ea8f854868475c2e3cef1fe09f8d175ec21a38 100644
--- a/mo/web/fields.py
+++ b/mo/web/fields.py
@@ -90,7 +90,7 @@ class Place(wtforms.StringField):
 
     place_loaded: bool = False
     place: Optional[db.Place] = None
-    place_error: str
+    place_error: str = ""
 
     def load_place(field) -> None:
         field.place = None
diff --git a/mo/web/org_contest.py b/mo/web/org_contest.py
index 3eb31e2eecdf393c9657e25483005fb6022f7fe7..3885a411d720fe2511237892fbbca25c12db05fe 100644
--- a/mo/web/org_contest.py
+++ b/mo/web/org_contest.py
@@ -1638,11 +1638,11 @@ def org_contest_edit(id: int):
 
 class ParticipantAddForm(FlaskForm):
     email = mo_fields.Email(validators=[validators.Required()])
-    first_name = mo_fields.FirstName(validators=[validators.Required()])
-    last_name = mo_fields.LastName(validators=[validators.Required()])
-    school = mo_fields.School(validators=[validators.Required()])
-    grade = mo_fields.Grade(validators=[validators.Required()])
-    birth_year = mo_fields.BirthYear(validators=[validators.Required()])
+    first_name = mo_fields.FirstName(validators=[validators.Optional()])
+    last_name = mo_fields.LastName(validators=[validators.Optional()])
+    school = mo_fields.School(validators=[validators.Optional()])
+    grade = mo_fields.Grade(validators=[validators.Optional()])
+    birth_year = mo_fields.BirthYear(validators=[validators.Optional()])
     participation_place = mo_fields.Place("Kód soutěžního místa")
     save = wtforms.SubmitField("Přidat")
 
diff --git a/mo/web/templates/doc_import.html b/mo/web/templates/doc_import.html
index 4a3055ee4b087dbff27d829529fcc6afe09d0085..163f9badd9770309f86d984b11b012593139bd48 100644
--- a/mo/web/templates/doc_import.html
+++ b/mo/web/templates/doc_import.html
@@ -20,16 +20,16 @@ když přidáte vlastní sloupce s novými názvy, budou se ignorovat.
 
 <h2>Import účastníků</h2>
 
-<p>Definovány jsou tyto sloupce (tučné jsou povinné):
+<p>Definovány jsou tyto sloupce (tučné jsou povinné, kurzívou jsou povinné pro zatím nezaregistrované účty):
 
 <table class=data>
 	<tr><th>Název<th>Obsah
 	<tr><td><b>email</b><td>E-mailová adresa
-	<tr><td><b>krestni</b><td>Křestní jméno
-	<tr><td><b>prijmeni</b><td>Příjmení
-	<tr><td><b>kod_skoly</b><td>Kód školy (viz katalog škol na tomto webu)
-	<tr><td><b>rocnik</b><td>Navštěvovaný ročník (třída). Pro základní školy je to číslo od 1 do 9, pro <i>k</i>-tý ročník <i>r</i>-leté střední školy má formát <i>k</i>/<i>r</i>.
-	<tr><td><b>rok_naroz</b><td>Rok narození
+	<tr><td><i>krestni</i><td>Křestní jméno
+	<tr><td><i>prijmeni</i><td>Příjmení
+	<tr><td><i>kod_skoly</i><td>Kód školy (viz katalog škol na tomto webu)
+	<tr><td><i>rocnik</i><td>Navštěvovaný ročník (třída). Pro základní školy je to číslo od 1 do 9, pro <i>k</i>-tý ročník <i>r</i>-leté střední školy má formát <i>k</i>/<i>r</i>.
+	<tr><td><i>rok_naroz</i><td>Rok narození
 	<tr><td>kod_mista<td>Pokud účastník soutěží někde jinde, je zde uveden kód oblasti, školy,
 		nebo speciálního soutěžního místa, kde se soutěž koná. Dozor na soutěžním místě
 		má pak právo odevzdávat za účastníka řešení.
diff --git a/mo/web/templates/org_contest_add_user.html b/mo/web/templates/org_contest_add_user.html
index 297d6ce7b40fb6c007f9b8fe18a7d587ec67e6c7..d544dbef1c6d75fe4f0bfc8b93788a3fb0a6cf33 100644
--- a/mo/web/templates/org_contest_add_user.html
+++ b/mo/web/templates/org_contest_add_user.html
@@ -11,8 +11,7 @@
 
 {% block body %}
 
-{% if errs %}
-{% endif %}
+<p>Jméno, škola, ročník a rok narození nejsou povinné pro již registrované účty.</p>
 
 {{ wtf.quick_form(form, form_type='simple', button_map={'save': 'primary'}) }}